Bash tips
Key bindings
- CTRL+F to move forward by a char
- CTRL+B to move backward by a char
- CTRL+A to jump to start of the line
- CTRL+E to jump to end of the line
- CTRL+K to kill the line starting from the cursor position
- ALT+D to delete a word starting from the current cursor position
- CTRL+W to remove the word backwards from cursor position
- CTRL+Y to paste text from the kill buffer
- CTRL+R to reverse search for commands you typed in the past from your history.
- CTRL+S to forward search (works in ZSH for me but not bash)- CTRL+F to move forward by a char
Case sensitive
-
bash like the rest of UNIX is case sensitive
-
MacOS & Windows filesystem is case insensitive
$SECONDS
Time the script/shell has been running for
# use uppercase
echo $SECONDS
logical operators
&&
, ||
can be used inside or outside double bracket [[
if [[ ($A -eq 0 ]] && [[ $C == "abc" ]]; then …
if [[ ($A -eq 0 || $B -ne 0) && $C == "abc" ]]; then …
$... substitutions
$()
, $(())
, ${}
usually return value of the commands inside
bracket [
, [[
, parentheses ()
, (())
, braces {}
-
[
single bracket calls programtest
, seeman test
var=abcdef if [ $var == abcdef ]; then echo yes else echo no fi
-
[[
double bracket does the same thing (basically) as a single bracket[
, but is a bash builtin. Particularly, it supports=~
for regular expressionif [[ $var == abcdef ]]; then if [[ $var =~ [a-z] ]]; then
-
()
parentheses create a subshell and run the commandspwd (cd /tmp; pwd) pwd
-
$()
returns the resultvar=$(cd /tmp; pwd)
-
(())
,$(())
arithmetic context to calculate numbersif (( a > b)) i = $((j + 3))
-
{}
groups commands and return concated output{date; top -b -n1 | head;} > logfile
-
${}
returns value of the variable named in the braces${var}_name # this is same as ${var_name} $var_name
[
can be used in plain sh. [[
and ((
are specific to bash (and ksh and zsh)
https://stackoverflow.com/questions/31255699/double-parenthesis-with-and-without-dollar
Sleep
sleep N
pause script for N seconds
Remove all except for one file
https://unix.stackexchange.com/questions/153862/remove-all-files-directories-except-for-one-file
ls | grep -xv "file.txt" | xargs rm
ls | grep -xv "file.txt" | parallel rm
from man grep:
-v, --invert-match
Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX)
-x, --line-regexp
Select only those matches that exactly match the
whole line. For a regular expression pattern, this
is like parenthesizing the pattern and then
surrounding it with ^ and $.
Shell Style Guide
https://google.github.io/styleguide/shellguide.html
变量相加
$((a + b))
数组长度
${#array}
String replace
${string/pattern/new}
replace${string//pattern/new}
replace all
message='The secret code is 12345'
echo ${message/[0-9]/X}
# The secret code is X2345
echo ${message//[0-9]/X}
# The secret code is XXXXX
获取文件列表
files=$(ls /path/to/*.txt)
echo $files
for f in $files
do
echo $f
done
Single quotes / double quotes
Single quotes won't interpolate anything, but double quotes will. For example, variables, expressions, backticks, certain \
escapes, etc.
Example
echo "$(echo "upg")"
# upg
echo '$(echo "upg")'
# $(echo "upg")
difference-between-single-and-double-quotes-in-bash
function
# $1, $2, ... $n, ..., $#
greeting() {
echo "Hello $1"
}
greeting "Joe"
result="$(greeting 'Joe')"
Check if a file or directory exists
使用条件判断和 -f
参数或 -d
参数
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
FILE=/ect/resolve.conf
if [ -f "$FILE" ]; then
echo "$FILE exists."
else
echo "$FILE does not exist"
fi
FILE=/etc/docker
if [ -d "$FILE" ]; then
echo "$FILE is a directory"
fi
How to Check if a File or Directory Exists in Bash | Linuxize
判断字符串为空(string is null or empty)
status=`git status | grep commit`
if [ ! -z "$status" ]; then
echo 'is not empty'
else
echo 'is empty'
fi
比较字符串 Compare string
-
[ string1 = string2 ]
: equal in[]
-
[[ string1 == string2 ]]
: equal in[[]]
-
string1 != string2
: not equal -
string1 =~ regex
: regex match -
-z string1
: zero length -
-n string1
: not zero length
a="Linux"
b="macos"
if [[ $a == $b ]]; then
echo "Strings are equal."
else
echo "Strings are not equal."
fi
if [[ $a =~ .*nux.* ]]; then
echo "It's there"
fi
for 循环
projects=(p1 p2)
for i in "${projects[@]}"; do
echo $i '-'
done
Local variable / environment variable
# Only in current shell
name=demo
# Can be accessed by process.env.NODE_ENV in script
export NODE_ENV=production
${}, $()
-
${var}
: 获取并代入变量的值 -
$(command)
: 执行命令并返回结果 -
`command`
: 执行命令并返回结果
Bash function
所有变量(包括函数内)默认是全局变量,除非在函数内用local
声明。
#!/bin/bash
greeting () {
echo "Hello $1"
}
greeting "Joe"
cd to the path of the script
cd "$(dirname "$0")"
获取 bash 脚本的 arguments
bash 函数通过这种方式拿到的是本身的参数。
copy.sh -from a.file -to b.file
$# # 4, total number
$0 # copy.sh, script file name
$1 # -from
$2 # a.file
$3 # -to
$4 # b.file
检查是否传递了参数
# 没有传递任何参数
if [[ $# -eq 0 ]]; then
echo "No arguments supplied"
fi
# 没有传递第一个参数
if [[ -z "$1" ]]; then
echo "No argument supplied"
fi
bash: Get OS type
if [[ "$OSTYPE" == "linux-gnu" ]]; then
# ...
elif [[ "$OSTYPE" == "darwin"* ]]; then
# ...
fi
os detection - How to detect the OS from a Bash script? - Stack Overflow
zsh tips
You have new mail.
mail
type number ID to read; d 1-9
to delete; x/q
to leave.
cd to history
cd -
cd -2
cd -3
cd -<tab>
Bash 批量文件重命名
-
Single file
mv [option] source destination mv file1.txt file2.txt
-
Multiple files with
sed
regexpfiles="*.wav" for f in $files; do newname=`echo "$f" | sed 's/[0-9]*//g'` mv "$f" "$newname" done
-
Multiple files
for f in *.html; do mv "$f" "${f%.html}.pug" done
${f%.html}
is using the Shell parameter expansion.${f%.html} 匹配末尾, ${f#../ouptput} 匹配开头
-
Multiple files with
find
find . -type f -name "*.html" -exec sh -c 'f="{}"; mv -- "$f" "${f%.html}.pug"' \;
Bash 支持换行符
printf 更通用
printf "hello\nworld"
echo 在不同环境不同版本可能不一致
echo 'Hello\nworld'
echo -e "Hello\nworld"
echo $'Hello\nworld'
出错立即退出脚本
默认情况下 shell 一条命令出错后仍会执行下面的命令,可以通过设置,在出错后立即退出脚本
set -e
# commands which fail will cause the script to exit immediately
bash -e my_script.sh