echo [option(s)] [string(s)]
$ echo i have a dream
i have a dream$ x=10
$ echo $x
10$ echo *
Business Card Template - PiXimperfect.psd Telegram Desktop Video desktop.ini echo.txt test1.txt test2.txt$ echo *.txt
echo.txt test1.txt test2.txt$ echo 'abc' > test.txt
$ cat test.txt
abc-e 选项识别字符串内的转义符 \\b 表示清除字符间空格$ echo -e 'a \bb \bc'
abc\n 表示换行$ echo -e 'a\nb\nc'
a
b
c\t 表示添加制表符$ echo -e 'a\tb\tc'
a b c可以结合 \n \t 使用:
$ echo -e '\ta\n\tb\n\tc'
a
b
c\v 表示添加垂直方向制表符$ echo -e 'a\vb\vc'
a
b
c\r 表示丢弃前面的字符串$ echo -e 'a\rbc'
bc\c 表示丢弃后面的字符串$ echo -e 'a\cbc'
aroot@localhost$以上就是 echo 命令的简单使用方法。
PS C:\Users\Marco Nie> "C:\Users\Marco Nie\Application\aria2\download-complete.ps1"
C:\Users\Marco Nie\Application\aria2\download-complete.ps1如以上,我想执行脚本,但是只是返回了路径。
解决方法是在脚本路径前加上程序调用符 & ,表示后面的是可执行文件。
&"C:\Users\Marco Nie\Application\aria2\download-complete.ps1"以上方式就可以正常执行脚本了。
还有就是脚本路径上如果有空格的话,需要使用引号将路径包围起来,这样就可以了。
参考链接:
http://www.splaybow.com/post/powershell-run-command-with-space.html
关于 aria2 的使用参考我的教程:https://blog.niekun.net/archives/1199.html
注意到配置文件里有一个:on-download-complete 选项,可以在下载完成后执行脚本,具体解释参考官方网页
可以利用这一点,编写一个脚本来触发系统通知,这样就可以知道文件下载完成了。
在 aria2 下载完成后,执行 on-download-complete 脚本时会自动传递三个参数:
示例:
$ cat hook.sh
#!/bin/sh
echo "Called with [$1] [$2] [$3]"
$ aria2c --on-download-complete hook.sh http://example.org/file.iso
Called with [1] [1] [/path/to/file.iso]这里使用 AppleScript 来编写简单的系统通知,使用 osascript 可以在 terminal 终端执行 AppleScript 脚本。
语法结构:
osascript [-l language] [-i] [-s flags] [-e statement | programfile] [argument ...]
这里主要使用 -e选项 来执行 AppleScript 脚本。
显示一个简单的通知很简单:
osascript -e 'display notification "hello world!"'
引号内的就是纯 AppleScript 脚本,很简单。
osascript -e 'display notification "hello world!" with title "This is the title"'
osascript -e 'display notification "hello world!" with title "Greeting" subtitle "More text"'
系统内置的声音音频在:/System/Library/Sounds 目录:
$ ls /System/Library/Sounds
Basso.aiff Frog.aiff Hero.aiff Pop.aiff Submarine.aiff
Blow.aiff Funk.aiff Morse.aiff Purr.aiff Tink.aiff
Bottle.aiff Glass.aiff Ping.aiff Sosumi.aifPop.aiff 这个声音就是默认的提示音。
osascript -e 'display notification "hello world!" with title "Greeting" subtitle "More text" sound name "Pop.aiff"'
osascript -e 'display alert "Hello World!" message "longer text can be added in the message field."'
除了显示系统通知外,也可以用音频提示,使用 say 命令来完成。
osascript -e 'say "Hello World!"'
提示信息将不显示而是语音播报出来。
download-complete.sh
#!/bin/sh
osascript -e 'display notification "download complete" sound name "Pop.aiff"'参考连接:
https://code-maven.com/display-notification-from-the-mac-command-line
journalctl 查看 nginx 日志时看到在每次启动服务后会出现一条错误信息:$ journalctl -u nginx
...
nginx.service: Failed to parse PID from file /opt/nginx/logs/nginx.pid: Invalid argument
...
查找了下原因,可能是 nginx 在启动时创建 nginx.pid 文件前 systemd 就在请求这个文件,所以出错了。
解决办法就是题前手动创建 systemd 需要的文件:
mkdir /etc/systemd/system/nginx.service.d
printf "[Service]\nExecStartPost=/bin/sleep 0.1\n" > /etc/systemd/system/nginx.service.d/override.conf
systemctl daemon-reload以上处理就可以解决问题。
参考链接:
https://bugs.launchpad.net/ubuntu/+source/nginx/+bug/1581864
python 官网:https://www.python.org/
当前最新版是 3.8.5,在这个页面找到地址:https://www.python.org/downloads/release/python-385/

下载 tgz 压缩包到本地并解压:
cd /tmp
wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz
tar xvf Python-3.8.5.tgz编译需要安装一些依赖:
apt install libffi-dev libgdbm-dev libsqlite3-dev libssl-dev zlib1g-dev
python 源码使用标准 GNU 编译系统,详细说明参考:https://blog.niekun.net/archives/883.html
将 python 安装到 /opt 目录,先创建文件夹:
mkdir /opt/python3.8.5
然后配置 configure:
cd /tmp/Python-3.8.5
./configure \
--prefix=/opt/python3.8.5 \
--enable-optimizations \没有错误提示的话就开始编译和安装:
make
make install
安装完成后测试执行:
/opt/python3.8.5/bin/python3 --version
返回版本信息则安装完成。
下面将可执行文件加入系统路径,创建软连接:
ln -s /opt/python3.8.5/bin/python3 /usr/bin/python
测试运行:
python --version
源码编译安装的 python 不自带 pip,需要自己安装,可以使用 get-pip.py 脚本来安装。
官网:https://pip.pypa.io/en/stable/installing/
下载脚本到本地:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py使用刚才安装的 python 执行脚本:
/opt/python3.8.5/bin/python3 get-pip.py
pip 的安装路径是 /opt/python3.8.5/bin/,测试命令:
/opt/python3.8.5/bin/pip3 --version
返回版本信息则安装完成。
添加软连接到系统路径:
ln -s /opt/python3.8.5/bin/pip3 /usr/bin/pip
测试命令:
pip --version
比如我们有一个 txt 文件:
ab.c 123 e.rt 456
oh.g 324 b.na 756
si.d 156 o.ui 452执行命令:
$ awk '{print $1}' test.txt
ab.c
oh.g
si.d可以看到返回结果为每一行的第一个字符串。默认以空格作为分隔符。
$1 为每行第一个字符串,$2 为每行第二个字符串,以此类推。$0 为整个文本。
可以同时输出多个内容:
$ awk '{print $1, $2}' test.txt
ab.c 123
oh.g 324
si.d 156awk 默认使用空格来分割字符串,也可以自己定义分割符:
$ awk 'FS = "." {print $1, $2}' test.txt
ab c 123 e
oh g 324 b
si d 156 o这时候,ab 和 c 123 e 分别是一个整体。
还有一种写法使用 -F 表示,要写在引号外部:
$ awk -F. '{print $1, $2}' test.txt
用 $NF 来表示每行最后一个串:
$ awk '{print $NF}' test.txt
456
756
452用 NF 来判断每行字符串格个数:只输出有 4 个字符串的所在行的内容
$ awk 'NF == 4 {print $1, $2}' test.txt
ab.c 123
oh.g 324
si.d 156NR 记录当前行的行号:
$ awk '{print NR}' test.txt
1
2
3$ awk '{print NR, $0}' test.txt
1 ab.c 123 e.rt 456
2 oh.g 324 b.na 756
3 si.d 156 o.ui 452字符串输出是可以自定义分割符号:
$ awk 'OFS="/" {print $1, $2}' test.txt
ab.c/123
oh.g/324
si.d/156BEGIN 规则是在 awk 读取输入文本前执行的指令,END 规则是在 awk 输出完字符串后执行的指令。
$ awk 'BEGIN {print "begin process"} {print $0} END {print "end process"}' test.txt
begin process
ab.c 123 e.rt 456
oh.g 324 b.na 756
si.d 156 o.ui 452
end process可以使用常用的判断来过滤输出结果:
设置第4个字符串数字需要大于等于500:
$ awk '$4 >=500 {print $0}' test.txt
oh.g 324 b.na 756设置行内必须包含字符串 ab:
$ awk '/ab/ {print $0}' test.txt
ab.c 123 e.rt 456设置行开始必须包含字符串 ab:
$ awk '/^ab/ {print $0}' test.txt
ab.c 123 e.rt 456如果命令很复杂,可以建立一个脚本来单独执行。
建立文件:test.awk
#!/usr/bin/awk -f
BEGIN {
# set the input and output field separators
FS=":"
OFS=":"
# zero the accounts counter
accounts=0
}
{
# set field 2 to nothing
$2=""
# print the entire line
print $0
# count another account
accounts++
}
END {
# print the results
print accounts " accounts.\n"
}可执行权限:
chmod +x test.awk
执行:
./test.awk /etc/passwd
windows 下也可以使用 awk,下载安装 Gawk 即可。
官网:http://gnuwin32.sourceforge.net/packages/gawk.htm
下载对应安装包,安装后将 awk 可执行文件的路径加入系统 PATH 即可使用,参考:https://blog.niekun.net/archives/413.html
https://www.howtogeek.com/562941/how-to-use-the-awk-command-on-linux/
http://linuxcommand.org/lc3_adv_awk.php
./test.sh abcdef abc.bbb
以上的命令使用了两个传入参数,abcdef,abc.bbb。
在脚本里使用时,$1 就表示第一个参数,$2 就表示第二个参数:
var1 = $1
var2 = $2在脚本中有一种用法,如:${1%def}jjj。
他的意思就是将 $1 最后的字符 def 替换为 jjj:
newstr1 = ${1%def}jjjnewstr1 的值就是 abcjjj。
newstr2 = ${2%.bbb}.cccnewstr2 的值就是 abc.ccc。
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
执行cmd命令并返回结果到字符串。
用法:
import subprocess
output = check_output(["cat", "/etc/hostname"]).strip()
print(output)以上脚本会执行 cat /etc/hostname 命令然后将结果赋值给 output 变量。strip() 可以将 string 的前后空格去掉。
它功能强大,用途广泛,大量用于视频网站和商业软件(比如 Youtube 和 iTunes),也是许多音频和视频格式的标准编码/解码实现。
最简单的方法就是用包管理工具如:apt 安装:
apt update
apt install ffmpeg
或者也可以从源码安装,可以参考我之前的教程:https://blog.niekun.net/archives/891.html
查看 ffmpeg 版本:
ffmpeg -version
查看支持的编码格式:如 h.264, h.265
ffmpeg -codecs
查看支持的容器:如 mp4, mp3, mkv
ffmpeg -formats
查看已安装的编码器:如 libx264, libx265, libvpx, aac
ffmpeg -encoders
FFmpeg 的命令行参数非常多,可以分成五个部分。
ffmpeg {1} {2} -i {3} {4} {5}
上面命令中,五个部分的参数依次如下:
全局参数
输入文件参数
输入文件
输出文件参数
输出文件参数太多的时候,为了便于查看,ffmpeg 命令可以写成多行:
$ ffmpeg \
[全局参数] \
[输入文件参数] \
-i [输入文件] \
[输出文件参数] \
[输出文件]下面是一个例子:
ffmpeg \
-y \ # 全局参数
-c:a libfdk_aac -c:v libx264 \ # 输入文件参数
-i input.mp4 \ # 输入文件
-c:v libvpx-vp9 -c:a libvorbis \ # 输出文件参数
output.webm # 输出文件上面的命令将 mp4 文件转成 webm 文件,这两个都是容器格式。输入的 mp4 文件的音频编码格式是 aac,视频编码格式是 H.264;输出的 webm 文件的视频编码格式是 VP9,音频格式是 Vorbis。
如果不指明编码格式,FFmpeg 会自己判断输入文件的编码。一般可以省略输入文件参数。
-c:指定编码器
-c copy:直接复制,不经过重新编码(这样比较快)
-c:v:指定视频编码器
-c:a:指定音频编码器
-i:指定输入文件
-an:去除音频流
-vn: 去除视频流
-preset:指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow。
-y:不经过确认,输出时直接覆盖同名文件。查看元数据信息,如时长,比特率等:
ffmpeg -i test.mp4
输出的信息较多,可以通过 -hide_banner 只显示媒体文件信息:
ffmpeg -i test.mp4 -hide_banner
转码,如 avi to h.264:
ffmpeg -i test.avi -c:v libx264 test.mp4
转换容器:
ffmpeg -i test.mp4 -c copy test.webm转换容器不需要转码,所以直接 copy 即可。
转换码率,转换成固定码率:
ffmpeg -i test.mp4 -b:v 500k test_out.mp4
转换码率,转换成一个码率范围:
ffmpeg -i test.mp4 -minrate 964K -maxrate 3856K -bufsize 2000K test_out.mp4
改变分辨率:转换成 480p
ffmpeg \
-i input.mp4 \
-vf scale=480:-1 \
output.mp4视频中提取音频:
ffmpeg \
-i input.mp4 \
-vn -c:a copy \
output.aac上面例子中,-vn 表示去掉视频,-c:a copy 表示不改变音频编码,直接拷贝。
视频截图:下面的例子是从指定时间开始,连续对1秒钟的视频进行截图
ffmpeg \
-y \
-i input.mp4 \
-ss 00:01:24 -t 00:00:01 \
output_%3d.jpg%3d 在 shell 里表示至少输出3个字符空间的数字:
% means "Print a variable here"
3 means "use at least 3 spaces to display, padding as needed"
d means "The variable will be an integer"如果只需要截一张图,可以指定只截取一帧。
$ ffmpeg \
-ss 01:23:45 \
-i input \
-vframes 1 -q:v 2 \
output.jpg上面例子中,-vframes 1 指定只截取一帧,-q:v 2 表示输出的图片质量,一般是1到5之间(1 为质量最高)。
裁剪:
裁剪(cutting)指的是,截取原始视频里面的一个片段,输出为一个新视频。可以指定开始时间(start)和持续时间(duration),也可以指定结束时间(end)。
$ ffmpeg -ss [start] -i [input] -t [duration] -c copy [output]
$ ffmpeg -ss [start] -i [input] -to [end] -c copy [output]下面是实际的例子。
# 从1分50秒开始截取10.5秒
ffmpeg -ss 00:01:50 -i test.mp4 -t 10.5 -c copy out.mp4
# 从25秒开始截取10秒
ffmpeg -ss 25 -i test.mp4 -to 10 -c copy out.mp4
ffmpeg -i test.mp4 -ss 25 -to 10 -c copy out.mp4上面例子中,-c copy 表示不改变音频和视频的编码格式,直接拷贝,这样会快很多。
使用的技术主要是 ffmpeg 的 2 pass 方法和 ffprobe 得到码率和时长信息。
bash脚本:
#!/bin/bash
target_video_size_MB="$2"
origin_duration_s=$(ffprobe -v error -show_streams -select_streams a "$1" | grep -Po "(?<=^duration\=)\d*\.\d*")
origin_audio_bitrate_kbit_s=$(ffprobe -v error -pretty -show_streams -select_streams a "$1" | grep -Po "(?<=^bit_rate\=)\d*\.\d*")
target_audio_bitrate_kbit_s=$origin_audio_bitrate_kbit_s # TODO for now, make audio bitrate the same
target_video_bitrate_kbit_s=$(\
awk \
-v size="$target_video_size_MB" \
-v duration="$origin_duration_s" \
-v audio_rate="$target_audio_bitrate_kbit_s" \
'BEGIN { print ( ( size * 8192.0 ) / ( 1.048576 * duration ) - audio_rate ) }')
ffmpeg \
-y \
-i "$1" \
-c:v libx264 \
-b:v "$target_video_bitrate_kbit_s"k \
-pass 1 \
-an \
-f mp4 \
/dev/null \
&& \
ffmpeg \
-i "$1" \
-c:v libx264 \
-b:v "$target_video_bitrate_kbit_s"k \
-pass 2 \
-c:a aac \
-b:a "$target_audio_bitrate_kbit_s"k \
"${1%.*}-$2mB.mp4"使用方法:压缩视频到 50 MB 大小
./script.sh test.mp4 50
使用的技术主要是 python,ffprobe 得到视频时长,然后计算需要切割为几个视频。
python 脚本:
#!/usr/bin/env python
import csv
import subprocess
import math
import json
import os
import shlex
from optparse import OptionParser
def split_by_manifest(filename, manifest, vcodec="copy", acodec="copy",
extra="", **kwargs):
if not os.path.exists(manifest):
print("File does not exist: %s" % manifest)
raise SystemExit
with open(manifest) as manifest_file:
manifest_type = manifest.split(".")[-1]
if manifest_type == "json":
config = json.load(manifest_file)
elif manifest_type == "csv":
config = csv.DictReader(manifest_file)
else:
print("Format not supported. File must be a csv or json file")
raise SystemExit
split_cmd = ["ffmpeg", "-i", filename, "-vcodec", vcodec,
"-acodec", acodec, "-y"] + shlex.split(extra)
try:
fileext = filename.split(".")[-1]
except IndexError as e:
raise IndexError("No . in filename. Error: " + str(e))
for video_config in config:
split_str = ""
split_args = []
try:
split_start = video_config["start_time"]
split_length = video_config.get("end_time", None)
if not split_length:
split_length = video_config["length"]
filebase = video_config["rename_to"]
if fileext in filebase:
filebase = ".".join(filebase.split(".")[:-1])
split_args += ["-ss", str(split_start), "-t",
str(split_length), filebase + "." + fileext]
print("########################################################")
print("About to run: "+" ".join(split_cmd+split_args))
print("########################################################")
subprocess.check_output(split_cmd+split_args)
except KeyError as e:
print("############# Incorrect format ##############")
if manifest_type == "json":
print("The format of each json array should be:")
print("{start_time: <int>, length: <int>, rename_to: <string>}")
elif manifest_type == "csv":
print("start_time,length,rename_to should be the first line ")
print("in the csv file.")
print("#############################################")
print(e)
raise SystemExit
def get_video_length(filename):
output = subprocess.check_output(("ffprobe", "-v", "error", "-show_entries",
"format=duration", "-of", "default=noprint_wrappers=1:nokey=1", filename)).strip()
video_length = int(float(output))
print("Video length in seconds: "+str(video_length))
return video_length
def ceildiv(a, b):
return int(math.ceil(a / float(b)))
def split_by_seconds(filename, split_length, vcodec="copy", acodec="copy",
extra="", video_length=None, **kwargs):
if split_length and split_length <= 0:
print("Split length can't be 0")
raise SystemExit
if not video_length:
video_length = get_video_length(filename)
split_count = ceildiv(video_length, split_length)
if(split_count == 1):
print("Video length is less then the target split length.")
raise SystemExit
split_cmd = ["ffmpeg", "-i", filename, "-vcodec",
vcodec, "-acodec", acodec] + shlex.split(extra)
try:
filebase = ".".join(filename.split(".")[:-1])
fileext = filename.split(".")[-1]
except IndexError as e:
raise IndexError("No . in filename. Error: " + str(e))
for n in range(0, split_count):
split_args = []
if n == 0:
split_start = 0
else:
split_start = split_length * n
split_args += ["-ss", str(split_start), "-t", str(split_length),
filebase + "-" + str(n+1) + "-of-" +
str(split_count) + "." + fileext]
print("About to run: "+" ".join(split_cmd+split_args))
subprocess.check_output(split_cmd+split_args)
def main():
parser = OptionParser()
parser.add_option("-f", "--file",
dest="filename",
help="File to split, for example sample.avi",
type="string",
action="store"
)
parser.add_option("-s", "--split-size",
dest="split_length",
help="Split or chunk size in seconds, for example 10",
type="int",
action="store"
)
parser.add_option("-c", "--split-chunks",
dest="split_chunks",
help="Number of chunks to split to",
type="int",
action="store"
)
parser.add_option("-S", "--split-filesize",
dest="split_filesize",
help="Split or chunk size in bytes (approximate)",
type="int",
action="store"
)
parser.add_option("--filesize-factor",
dest="filesize_factor",
help="with --split-filesize, use this factor in time to"
" size heuristics [default: %default]",
type="float",
action="store",
default=0.95
)
parser.add_option("--chunk-strategy",
dest="chunk_strategy",
help="with --split-filesize, allocate chunks according to"
" given strategy (eager or even)",
type="choice",
action="store",
choices=['eager', 'even'],
default='eager'
)
parser.add_option("-m", "--manifest",
dest="manifest",
help="Split video based on a json manifest file. ",
type="string",
action="store"
)
parser.add_option("-v", "--vcodec",
dest="vcodec",
help="Video codec to use. ",
type="string",
default="copy",
action="store"
)
parser.add_option("-a", "--acodec",
dest="acodec",
help="Audio codec to use. ",
type="string",
default="copy",
action="store"
)
parser.add_option("-e", "--extra",
dest="extra",
help="Extra options for ffmpeg, e.g. '-e -threads 8'. ",
type="string",
default="",
action="store"
)
(options, args) = parser.parse_args()
def bailout():
parser.print_help()
raise SystemExit
if not options.filename:
bailout()
if options.manifest:
split_by_manifest(**(options.__dict__))
else:
video_length = None
if not options.split_length:
video_length = get_video_length(options.filename)
file_size = os.stat(options.filename).st_size
split_filesize = None
if options.split_filesize:
split_filesize = int(
options.split_filesize * options.filesize_factor)
if split_filesize and options.chunk_strategy == 'even':
options.split_chunks = ceildiv(file_size, split_filesize)
if options.split_chunks:
options.split_length = ceildiv(
video_length, options.split_chunks)
if not options.split_length and split_filesize:
options.split_length = int(
split_filesize / float(file_size) * video_length)
if not options.split_length:
bailout()
split_by_seconds(video_length=video_length, **(options.__dict__))
if __name__ == '__main__':
main()使用方法:将视频切割为单个视频100秒
./split.py -f test.mp4 -s 100
ffprobe 可以用来得到视频信息。
视频时长:秒
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
视频码率:bit
ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 input.mp4
http://www.ruanyifeng.com/blog/2020/01/ffmpeg.html
https://stackoverflow.com/questions/29082422/ffmpeg-video-compression-specific-file-size
https://github.com/c0decracker/video-splitter
https://trac.ffmpeg.org/wiki/FFprobeTips