另一个 Bash 速查表
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
基础知识
凡事皆有起步……请注意,某些命令可能需要 bash 4(尽管绝大多数代码片段都是针对 bash 3 的)。
Bash 是一个缩写词
Bash 是“Bourne Again Shell”的缩写,意为“重生的外壳”。事实上,Bash 已经取代了最初于 1979 年发布的旧款 Bourne 外壳。
Bash 是一种“通用型”脚本语言
Bash 是一种非常强大的语言,你可以学习它来自动化各种任务,无论是个人使用、服务器维护还是 DevOps 操作。Bash 脚本编写可以是在纯文本文件中编写一系列命令行。
请注意,您也可以在终端中输入这些命令。您可以使用现有的命令,例如grep`sudo`、 `sudo` 和 `sudo` sed,find还可以使用您可能已安装的第三方库中的其他命令。
不过,将所有指令整理到可以使用 Git 打包的文件中,然后从一台机器传输到另一台机器或在多台服务器上运行,通常更方便。Bash 脚本通常是没有文件扩展名的文件,您可以这样执行:
bash myscript
或者,您可以将脚本设置为可执行文件并运行它:
chmod +x myscript && ./myscript
Bash 不是 sh!
虽然 Bash 可以运行大多数 sh 脚本(语法非常相似),但 sh 并不能处理所有 Bash 命令。
以下命令行可能有效,但它们有点误导性:
bash mysh.sh
sh myscript
别忘了谢邦
它位于#!bash 文件的最顶部。“Bang”代表感叹号“!”。
#!/usr/bin/env bash
上面这行代码一点也不花哨!它将脚本链接到 Bash 解释器的绝对路径。/usr/bin/env这通常是一个明智的选择,因为它会获取用户 $PATH 环境变量中设置的可执行文件。
写评论
使用#:
# comment
变量
在 Bash 中,变量的声明、使用和操作都很容易。
内置变量(并非完整列表)
echo "$1 $2" # positional arguments passed one by one, e.g., bash myscript arg1 arg2
echo "$#" # number of arguments passed
echo "$@" # all arguments passed
echo "You are here: $PWD" # $PWD is the current path
分配新变量
赋值变量时,要用=符号括起来,符号前后不要有空格:
MyVar="My string"
MyArray=(all in one)
使用变量
您可以使用带$符号的已声明变量:
echo $Variable
echo "This is my var: $Variable" # double quotes are required for interpolation
echo "${MyArray[@]:0:3}" # "all in one", because it prints 3 elements starting from the first (0)
# Be careful the following does not display a value
echo ${#MyArray[2]} # "3" as there are 3 chars in the third element of the array, which is the word "one"
修改变量
您可以使用以冒号 (:) 括起来的各种符号来修改已声明的变量{}。
MyVar="My variable"
# Length
echo ${#MyVar}
# Substitution
echo ${MyVar//a/O} # "My vOriOble"
# Expansion
echo ${!BASH@} # all variable names than begin with "BASH"
echo ${!BASH*} # all variable names than begin with "BASH"
# Removals
MyFile="list.txt"
echo ${MyFile%.*} # the filename without extension
echo ${MyFile##*.} # the file's extension
# Default value
echo ${AnotherOne:-"Another one"} # displays "Another one" even if AnotherOne is not defined
字典
declare -A Movies
Movies[title]="The Dark Knight Rises"
Movies[bestActress]="Anne Hathaway"
Movies[director]="Christopher Nolan"
printf
printf是类似 C 语言的打印预格式化文本的方法。它提供了比简单的 `print` 方法更高级的功能echo。它接受格式和参数作为参数,并打印格式化后的文本。
例如,您可以在一行中指定类型和一些格式选项:
printf "%s\n" "Learn it" "Zip it" "Bash it" # it prints 3 lines
printf "%.*f\n" 2 3.1415926 # prints 3.14
循环,循环,循环
为了
for i in "${MyArray[@]}"; do
echo "$i"
done
它也适用于字符串:
MyString="First Second Third"
for n in $MyString
do
echo "$n line"
done
尽管
while [ true ]
do
echo "We loop"
break
done
方便的炉灶
echo {1..7} # 1 2 3 4 5 6 7
echo {a..g} # a b c d e f g
直到
until [ $counter == 111 ]
do
echo "Number is: $((counter++))"
done
获取用户输入
echo "6x7?"
read answer
echo answer # hopefully 42 but can be anything the user wants
条件语句
if [[ CONDITION1 || CONDITION2 ]]
then
# code
elif [[ CONDITION3 && CONDITION4 ]]
then
# code
else
# code
fi
或者,您可以使用 case 语句:
case $COLOR in
Red)
echo -n "red"
;;
Blue | Green)
echo -n "blue or green"
;;
Yellow | "Sunny" | "Gold" | "Fire")
echo -n "Yellow"
;;
*)
echo -n "What?"
;;
esac
错误及退出策略
最好能够提出错误提示,以防止脚本被滥用。
如果命令失败,则立即退出。
#!/usr/bin/env bash
set -e
N出口
您可以退出脚本并指定退出代码:
#!/usr/bin/env bash
if [ CONDITION ]; then
exit 0 # 0 1 2 3 ... N
fi
0 表示成功,而 {1,2,3,N} 表示错误。
测试错误
# $? is the exit status of last command
if [[ $? -ne 0 ]] ; then
echo "You failed!"
exit 1
else
echo "You are the best!"
fi
调试 bash 脚本
#!/usr/bin/env bash
set -x # set -n can also be used to check syntax errors
ARGS=("$@") # store all script args in a var
-x您也可以在终端中使用以下选项执行脚本:
bash -x myscript
文件系统和目录
您可以在 bash 脚本中使用所有基本的文件系统命令,例如cp、rm、ls或。mvmkdir
首先要检查文件是否可执行:
if [ ! -x "$PWD/myscript" ]; then
echo "File is not executable!"
exit 1
fi
可-f用于文件、-d目录、-e是否存在等。选项非常多,您甚至可以使用(比……新)和(比……旧)选项测试符号链接-L并按日期比较文件。-nt-ot
更高级的用法
以下是一些我认为比基本技巧稍微复杂一些的技巧。
充分利用 bash 语法的优势
Bash 非常方便,可以快速构建输出,尤其是在使用花括号展开的情况下:
echo {1..10}{0,5}h # bash 3
echo {10..120..10}km # requires bash 4
定义函数
function test ()
{
echo "$1 $2" # displays positional arguments passed to the function ("All" "In")
return 0 # if you return a value, it !must be numerical!
}
test "All" "In"
瞄准镜
在 Bash 中,你不需要声明变量。如果你调用一个不存在的变量,不会得到任何错误,只会返回一个空值。
环境变量
环境变量是在系统级别定义的。它们存在于所有进程中。
要列出它们,请使用printenv:
printenv USER # if you don't specify a variable, all variables will be displayed
Shell 变量
以下赋值定义了一个 shell 变量:
TEST="My test"
export你可以在脚本的任何位置直接使用它,包括函数内部。但是,除非你使用命令来传递此类信息,否则它们不会传递给子进程。
关键词local
该local关键字只能在函数内部使用:
function test ()
{
local MyVar="My string" # local variable
}
test
你不能导出局部变量。
最佳实践
一般来说,最好使用局部变量和函数。全局变量应该谨慎使用,避免不必要的覆盖和混淆。
循环遍历命令的结果
for r in $(ls $PWD)
do
# task using the result of the command => r
echo "$r"
done
使用函数的输出
function hello() {
echo "Hello Ethan"
}
echo "This is the result of the function : $(hello)"
将命令结果存储在变量中
Users=$(cat users.txt)
捕获“是/否”输入
read -p "Continue? [y/n]: " -n 1 -r
echo # extra line
if [[ $REPLY =~ ^[Yy]$ ]]
then
echo "ok"
elif [[ $REPLY =~ ^[Nn]$ ]]
then
echo "Too bad!"
else
echo "Sorry, I did not understand your answer :("
fi
捕获用户的选择
以下bash代码提示用户进行选择并捕获答案:
select w in "zsh" "bash" "powershell"
do
echo "You prefer $w"
break
done
别名和覆盖命令
您可以创建特殊别名:
alias lz='ls -alh'
您还可以通过重新定义命令来覆盖现有命令。
链式命令
wget -O /tmp/logo.jpg 'https://dev-to-uploads.s3.amazonaws.com/uploads/logos/resized_logo_UQww2soKuUsjaOGNB38o.png' && echo "echo only if the first hand is true"
wget -O /tmp/logo2.jpg 'https//example.com/logo.jpg' || echo "echo only if the first hand is wrong"
wget -O tmp/logo3.jpg 'https//example.com/logo.jpg' ; echo "echo whatever happens"
困难模式
这里有一些你可能会觉得有用的 Bash 高级概念。
执行上次执行的命令
sudo !!
重定向、标准错误输出、标准输出
COMMAND > out.txt # write in out.txt
COMMAND >> out.txt # append in out.txt
COMMAND 2> test.log # stderr to test.log
COMMAND 2>&1 # stderr to stdout
COMMAND &>/dev/null # stdout and stderr to (null)
替换COMMAND成你的命令。
这是一个Trap!
该trap命令采用了一种更高级的错误处理方法。理解它的关键在于忘记异常处理以及你在其他语言中可能了解的类似概念。
其理念是仅在特定条件下执行某些操作:
#!/usr/bin/env bash
trap 'catch' EXIT
catch() {
echo "Exit game!"
}
echo "ok"
echo "---"
exit 0
“退出游戏!”这条信息之所以会显示,仅仅是因为exit 0结尾处有逗号。
更实际地说,这种用法并不罕见:
#!/usr/bin/env bash
trap "rm /tmp/scan.txt" EXIT
在 Bash 脚本中执行脚本
使用以下source命令:
#!/usr/bin/env bash
source ./otherScript
亚壳层
子 shell 是命令处理器的一个独立实例。shell 脚本本身可以启动子进程。这些子 shell 允许脚本进行并行处理,实际上是同时执行多个子任务。
换句话说,括号内的命令会在子 shell 中执行。这在一定程度上解释了export将环境变量传递给子进程的命令的用途。
有时你会看到“叉子”这个词,但它的意思是一样的。
管道故障
您可以采用以下更高级的错误处理方法:
#!/usr/bin/env bash
set -eu # u is to exit if using undefined variables
set -o pipefail
上述设置命令不仅启用“错误模式”,而且还能防止管道中的任何错误静默发生,这是不设置该-o pipefail选项时的默认行为。
我在终端里最喜欢的技巧
以下命令可能更偏向 Unix 而不是纯粹的 bash 脚本,但这并非完全偏离主题🤞🏻。
使用bash
您可能需要安装第三方终端提示符解决方案或使用 zsh。只需输入:
bash
按下回车键,就会进入 bash 提示符。然后你可以输入命令man bash来显示帮助信息。
使用\
\在长命令行末尾使用可以提高可读性,避免分散注意力。
使用&
nohup bash script &
上述方法允许在后台执行 bash 脚本并捕获挂断信号。
杂项
我经常使用的一些技巧:
仅列出当前文件夹中的子目录
SUBDIRS=$(ls -d */)
for sub in $SUBDIRS
do
echo $sub
done
快速重命名文件
mv /project/file.txt{,.bak} # rename file.txt.bak
在远程服务器上执行本地 bash 脚本
ssh REMOTE_HOST 'bash -s' < myScript
查找大文件(大于 10MB)
find . -size +10M -print
快速创建多个文件
您可以输入:
touch {script1,script2,script3,script4,script5,script6,script7}
但这仍然有点繁琐,所以请使用这个方法:
touch script{1..7}
黑客专用 Bash
黑客肯定会使用 Bash。虽然 Python 在渗透测试方面非常便捷,但 Bash 也能帮助自动化许多任务。专业人士创建自定义 Bash 脚本来加快分析速度的情况并不少见。
你好世界:扫描脚本
与其一遍又一遍地重复相同的命令行,不如将它们分组到特定的文件中。大多数安全二进制文件都是这样构建的。
你可以使用该read命令使脚本更易于使用,或者直接传递参数。让我们给脚本命名。scanning
#!/usr/bin/env bash
echo "Welcome aboard"
echo "What's the targeted IP?"
read TargetIp
if [[ ! $TargetIp ]]
then echo "Missing targeted IP!"
exit 1
fi
echo "What port do you want to scan?"
read PortNumber
if [[ ! $PortNumber ]]
then echo "Missing port number!"
exit 1
fi
nmap $TargetIp -p $PortNumber >> scan.txt # append results in scan.txt
然后我们就可以运行了bash scanning。当然,现在已经有一些工具可以简化 nmap 的使用,而且这只是一个非常不完整的例子,但你应该能明白其中的原理。
别名
攻击者在使用终端中的高级命令时很可能会使用别名:
alias whatsMyIp="echo $(ifconfig -a | grep broadcast | awk '{print $2}')"
whatsMyIp # IP local
虽然渗透测试发行版可能具有更好的自动完成功能,但你并不总是在这种优化的环境中操作,因此别名可以节省大量时间和精力。
黑客攻击 bash
黑客可以像攻击其他任何语言一样攻击 Bash。看看 hacktricks 的这篇文章就知道了。