~/tmp$ false || echo"Oops, fail" Oops, fail ~/tmp$ true || echo"Will not be printed" ~/tmp$ true && echo"Things went well" Things went well ~/tmp$ false && echo"Will not be printed" ~/tmp$ true ; echo"This will always run" This will always run ~/tmp$ false ; echo"This will always run" This will always run
命令置换和进程替换
一种常见的场景是希望将命令的输出结果作为变量获取,我们可以通过 命令置换command substitution 完成。当我们输入 $(CMD) 时,它会先执行 CMD,并用结果取代原本的 $(CMD)。比如,当我们想要遍历文件夹时,可以使用 for file in $(ls),shell 会先调用 ls 然后迭代访问这些其返回结果。
echo"Starting program at $(date)"# Date will be substituted
echo"Running program $0 with $# arguments with pid $$"
for file in"$@"; do grep foobar "$file" > /dev/null 2> /dev/null # When pattern is not found, grep has exit status 1 # We redirect STDOUT and STDERR to a null register since we do not care about them if [[ $? -ne 0 ]]; then echo"File $file does not have any foobar, adding one" echo"# foobar" >> "$file" fi done
代码中比较了前一条命令的返回码是否为 0,如果想要执行其他比较,可以参考 Linux 手册 和 Unix wiki,执行比较的时候注意加双中括号 [[]]。
~/tmp$ mkdir foo bar ~/tmp$ mkdir {foo,bar}/{x..z} ~/tmp$ tree . . ├── bar │ ├── x │ ├── y │ └── z ├── foo │ ├── x │ ├── y │ └── z ├── mcd.sh └── test.sh
一般用 -h 或 --help,也可以用 man 直接查看使用手册,但有时候手册过于臃肿,我们可以借助工具 tldr 将说明简化。
查找文件
直接使用内置命令 find 即可:
1 2 3 4 5 6 7 8
# Find all directories named src find . -name src -type d # Find all python files that have a folder named testin their path find . -path '*/test/*.py' -type f # Find all files modified in the last day find . -mtime -1 # Find all zip files with size in range 500k to 10M find . -size +500k -size -10M -name '*.tar.gz'
我们还可对找到的文件执行操作:
1 2 3 4
# Delete all files with .tmp extension find . -name '*.tmp' -exec rm {} \; # Find all PNG files and convert them to JPG find . -name '*.png' -exec convert {} {}.jpg \;
~/tmp$ grep -R "s" 17s ./script.py:#!/usr/bin/python3 ./script.py:import sys ./script.py:for arg in reversed(sys.argv[1:]): ./test.sh:#!/bin/bash ./test.sh:echo "Starting program at $(date)" # Date will be substituted ./test.sh:echo "Running program $0 with $# arguments with pid $$" ./test.sh: # When pattern is not found, grep has exit status 1 ./test.sh: # We redirect STDOUT and STDERR to a null register since we do not care about them ./test.sh: echo "File $file does not have any foobar, adding one" ~/tmp$ rg "s" test.sh 1:#!/bin/bash 3:echo "Starting program at $(date)" # Date will be substituted 5:echo "Running program $0 with $# arguments with pid $$" 9: # When pattern is not found, grep has exit status 1 10: # We redirect STDOUT and STDERR to a null register since we do not care about them 12: echo "File $file does not have any foobar, adding one"
script.py 1:#!/usr/bin/python3 2:import sys 3:for arg in reversed(sys.argv[1:]):
查找 shell 命令
通过 history 我们可以浏览之前输入的命令,除了不断按上箭头,我们还可以通过 history | grep find 找到包含 “find” 子串的命令。
~/tmp$ cat marco.sh marco() { target=~/tmp/test.txt if [[ ! (-e $target) ]]; then touch $target fi echo $(pwd) > $target } ~/tmp$ cat polo.sh polo() { cd $(cat ~/tmp/test.txt) }
注意修改执行权限并用 source 将这两个命令定义给 shell。
练习 3
捕获脚本的全部输出打印到文件,记录运行次数
1 2 3 4 5 6 7 8 9 10 11 12
#!/usr/bin/env bash
for (( i=1;; i++ )); do n=$(( RANDOM % 100 )) if [[ n -eq 42 ]]; then echo"Something went wrong" >&2 echo"The error was using magic numbers" echo"run $i times" exit 1 fi echo"Everything went according to plan" done