【发布时间】:2010-09-17 16:53:56
【问题描述】:
Shell 脚本通常用作粘合剂,用于自动化和简单的一次性任务。您最喜欢 Bash shell/脚本语言的哪些“隐藏”功能?
- 每个答案一个功能
- 提供功能示例和简短说明,而不仅仅是文档链接
- 使用粗体标题作为第一行标记功能
另见:
【问题讨论】:
标签: bash shell scripting hidden-features
Shell 脚本通常用作粘合剂,用于自动化和简单的一次性任务。您最喜欢 Bash shell/脚本语言的哪些“隐藏”功能?
另见:
【问题讨论】:
标签: bash shell scripting hidden-features
插入前一行的最后一个参数
alt-. 有史以来最有用的组合键,试试看,不知为何没人知道这个组合键。
一次又一次地按下它以选择较旧的最后一个参数。
当您想对刚才使用的东西做其他事情时非常棒。
【讨论】:
!$ 很难快速输入。我总是不得不放慢脚步,考虑把美元符号放在第二位。 Alt+. 更快更容易。更不用说,您实际上可以在执行之前看到文本。
如果您想在注销后保持进程运行:
disown -h <pid>
是一个有用的内置 bash。与nohup 不同,您可以在已经运行的进程上运行disown。
首先,使用 control-Z 停止工作,从 ps 获取 pid(或使用 echo $!),使用 bg 将其发送到后台,然后使用带有 -h 标志的 disown。
不要忘记后台你的工作,否则当你注销时它会被杀死。
【讨论】:
jobs -l(或-p)获取PID
fork() 和 exec() 它的参数,本质上是一个守护进程。我可以轰炸外壳并一口气运行一些东西bgexec google-chrome && exit。
screen 的功能一样吗?
手册中 EXPANSION 部分列出的几乎所有内容
特别是参数扩展:
$ I=foobar
$ echo ${I/oo/aa} #replacement
faabar
$ echo ${I:1:2} #substring
oo
$ echo ${I%bar} #trailing substitution
foo
$ echo ${I#foo} #leading substitution
bar
【讨论】:
我最喜欢的:
sudo !!
使用 sudo 重新运行上一个命令。
【讨论】:
Ctrl + r 在您的命令历史记录中开始“反向增量搜索”。当您继续键入时,它会检索包含您输入的所有文本的最新命令。
Tab 补全您目前输入的单词,如果它没有歧义的话。
Tab Tab 列出您目前输入的单词的所有补全。
Alt + * inserts 所有可能的补全,这特别有用,例如,如果您刚刚输入了具有潜在破坏性的命令通配符:
rm -r source/d*.c Alt + *rm -r source/delete_me.c source/do_not_delete_me.c
Ctrl + Alt + e 对当前行执行别名、历史记录和 shell 扩展。换句话说,当前行被重新显示,因为它将被 shell 处理:
ls $HOME/tmp Ctrl Alt + els -N --color=tty -T 0 /home/cramey
【讨论】:
取回历史命令和参数
可以使用! 运算符选择性地访问以前的命令和参数。当您处理长路径时,它非常有用。
您可以使用history 检查您的最后命令。
您可以使用以前的命令,!<n> 是 n 中的命令在 history 中的索引,负数从历史上的最后一个命令开始倒数。
ls -l foo bar
touch foo bar
!-2
!:<n> 可以使用之前的参数,0 是命令,>= 1 是参数。
ls -l foo
touch !:2
cp !:1 bar
您可以将两者与!<n>:<m>结合使用
touch foo bar
ls -l !:1 !:2
rm !-2:1 !-2:2
!-2
您还可以使用参数范围!<n>:<x>-<y>
touch boo far
ls -l !:1-2
其他! 特殊修饰符有:
* 用于所有参数
ls -l foo bar
ls !*
^ 用于第一个参数 (!:1 == !^)
$ 最后一个参数
ls -l foo bar
cat !$ > /dev/null
【讨论】:
我喜欢 -x 功能,它允许查看脚本中发生了什么。
bash -x script.sh
【讨论】:
SECONDS=0; sleep 5 ; echo "that took approximately $SECONDS seconds"
秒
每次这个参数 引用,秒数 因为返回了 shell 调用。 如果给 SECONDS 赋值, 随后返回的值 参考是秒数 因为赋值加上值 分配的。如果 SECONDS 未设置,则 失去了它的特殊属性,即使 它随后被重置。
【讨论】:
这是我的最爱之一。这将选项卡完成设置为不区分大小写。它非常适合快速输入目录路径,尤其是在文件系统默认不区分大小写的 Mac 上。我把它放在我的主文件夹中的.inputrc 中。
set completion-ignore-case on
【讨论】:
特殊变量random:
if [[ $(($RANDOM % 6)) = 0 ]]
then echo "BANG"
else
echo "Try again"
fi
【讨论】:
$ sigil;无需单独评估和测试:if (( RANDOM % 6 == 0 )); then echo "BANG"; else echo "Try again"; fi 甚至更短:(( RANDOM % 6 )) && echo "Try again" || echo "BANG"。
正则表达式处理
最近 bash 发布的功能正则表达式匹配,所以你可以这样做:
if [[ "mystring" =~ REGEX ]] ; then
echo match
fi
其中 REGEX 是格式由 man re_format 描述的原始正则表达式。
任何括号部分的匹配都存储在 BASH_REMATCH 数组中,从元素 1 开始(元素 0 是整个匹配的字符串),因此您也可以使用它来进行正则表达式驱动的解析。
【讨论】:
Ctrlx Ctrle
这会将当前命令加载到变量 VISUAL 中定义的编辑器中。这对于此处列出的一些长命令非常有用。
使用 vi 作为编辑器:
export VISUAL=vi
【讨论】:
set -o vi 然后 Esc 命令进入内联编辑,简单的“v”将命令拉入完整的 vi 编辑器。
快速和肮脏的错别字更正(特别适用于慢速连接上的长命令,因为使用命令历史并滚动浏览它会很糟糕):
$ cat /proc/cupinfo
cat: /proc/cupinfo: No such file or directory
$ ^cup^cpu
还可以尝试!:s/old/new,它在上一个命令中将旧的替换为新的一次。
如果您想替换多次出现,您可以使用!:gs/old/new 进行全局替换。
您可以将gs 和s 命令用于任何历史事件,例如
!-2:s/old/new
在倒数第二个命令中将old 替换为new(一次)。
【讨论】:
man bash 中的事件指示符和修饰符。尝试!:s/old/new 将旧命令替换为新命令一次。如果要替换多次出现,可以使用!:gs/old/new 进行全局替换。这可以与 James 的帖子 (stackoverflow.com/questions/211378/hidden-features-of-bash/…) 结合使用,例如:!$:s/old/new(在上一个命令的最后一个参数中用 new 代替 old),!-2:0:gs/a/s !-2*(在倒数第二个命令名称中用 s 代替每次出现的 a并添加倒数第二个命令的所有参数)。祝你好运!
这是我最喜欢的两个:
要检查没有真正执行脚本的语法,请使用:
bash -n script.sh
返回上一个目录(是的,我知道 pushd 和 popd,但这样更快)
cd -
【讨论】:
使用中缀布尔运算符
考虑简单的 if:
if [ 2 -lt 3 ]
then echo "Numbers are still good!"
fi
那个 -lt 看起来有点丑。不是很现代。如果您在布尔表达式周围使用双括号,则可以使用普通的布尔运算符!
if [[ 2 < 3 ]]
then echo "Numbers are still good!"
fi
【讨论】:
数组:
#!/bin/bash
array[0]="a string"
array[1]="a string with spaces and \"quotation\" marks in it"
array[2]="a string with spaces, \"quotation marks\" and (parenthesis) in it"
echo "There are ${#array[*]} elements in the array."
for n in "${array[@]}"; do
echo "element = >>${n}<<"
done
可以在Advanced Bash-Scripting Guide 中找到有关数组(和其他高级 bash 脚本内容)的更多详细信息。
【讨论】:
在显示 bash 提示符之前运行命令
在“PROMPT_COMMAND”环境变量中设置一个命令,它将在每个提示之前自动运行。 示例:
[lsc@home]$ export PROMPT_COMMAND="date"
Fri Jun 5 15:19:18 BST 2009
[lsc@home]$ ls
file_a file_b file_c
Fri Jun 5 15:19:19 BST 2009
[lsc@home]$ ls
对于下一个愚人节,将“export PROMPT_COMMAND=cd”添加到某人的 .bashrc,然后坐下来观察混乱的发展。
【讨论】:
man 页面的魔法组合键:Ctrl + a 和 Ctrl + e 将光标移动到分别是当前行。
Ctrl + t 和 Alt + t 用当前的,然后将光标向前移动。
Alt + u 和 Alt + l 转换当前单词(从光标到结尾)大写和小写。
提示:按 Alt + – 后跟这些命令中的任何一个来转换当前的 开始单词。
man 提示:查看man 页面时,使用/ 在页面中搜索文本。使用 n 跳转到下一场比赛或使用 N 跳转到上一场比赛。
利用其格式,加快您在 man 页面中搜索特定命令或子部分的速度:
o 不要键入 /history expansion 来查找该部分,而是尝试 /^history,使用插入符号 (^) 仅查找 以“历史”开头。
o 尝试使用 / read 并在前面加上几个空格,以搜索该内置命令。内置函数始终在 man 页面中缩进。
【讨论】:
export TMOUT=$((15*60))
在 15 分钟空闲时间后终止 bash,设置为 0 以禁用。我通常把它放在我的根帐户上的 ~/.bashrc 中。它在管理您的盒子时很方便,您可能会在离开终端之前忘记注销。
【讨论】:
C-S-- Control Shift Minus 撤消键入操作。
任何删除操作Cw(删除前一个单词),Ck(删除到行尾),Cu(删除到行首) 等... 将已删除的文本复制到终止环,您可以使用以下命令粘贴最后一个终止:Cy 并使用 Alt-y 循环(并粘贴)已删除项目的环
【讨论】:
通过设置FIGNORE 变量,您可以在制表符完成时忽略某些文件。
例如,如果您有一个 subverion 存储库,并且想要更轻松地导航,则可以这样做
export FIGNORE=".svn"
现在您可以cd 而不会被.svn 目录阻止。
【讨论】:
使用算术:
if [[ $((2+1)) = $((1+2)) ]]
then echo "still ok"
fi
【讨论】:
大括号扩展
使用 {x,y,z} 进行标准扩展:
$ echo foo{bar,baz,blam}
foobar foobaz fooblam
$ cp program.py{,.bak} # very useful with cp and mv
用 {x..y} 进行序列扩展:
$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
$ echo {a..f}{0..3}
a0 a1 a2 a3 b0 b1 b2 b3 c0 c1 c2 c3 d0 d1 d2 d3 e0 e1 e2 e3 f0 f1 f2 f3
【讨论】:
我最近阅读了Csh Programming Considered Harmful,其中包含这个惊人的宝石:
考虑管道:
A | B | C
你想知道 C 的状态,嗯,很简单:它在 $? 中,或者 csh 中的 $ 状态。但是如果你想从 A 那里得到它,那你就不走运了——如果 你在 csh 中,就是这样。在 Bourne shell 中,你可以得到它,虽然 这样做有点棘手。 这是我在运行 dd 时必须做的事情 stderr 进入 grep -v 管道以消除记录输入/输出噪音,但有 返回 dd 的退出状态,而不是 grep 的:
device=/dev/rmt8
dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'
exec 3>&1
status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) |
egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`
exit $status;
【讨论】:
截断文件的内容(归零文件)
> file
具体来说,这对于截断日志文件非常有用,当文件被另一个进程打开时,仍然可能写入文件。
【讨论】:
set -o noclobber。然后你需要使用>| file 来截断一个文件。
不是一个真正的功能,而是一个方向:我在commandlinefu.com 发现了许多“隐藏功能”、秘密和各种 bash 实用性。这个答案的许多评价最高的答案,我在那个网站上学到的:)
【讨论】:
另一个小的: Alt+#
退出当前行并将其移入历史缓冲区。
因此,当您组装命令行时,您需要发出临时命令,例如找到一个文件,你只需按 alt+#,发出另一个命令,进入历史记录,取消注释并继续。
【讨论】:
用大括号代替 for 循环中的 do 和 done
For 循环体通常在do...done(只是一个例子):
for f in *;
do
ls "$f";
done
但我们可以使用大括号来使用 C 风格:
for f in *; {
ls "$f";
}
我认为这看起来比do...done更好,我更喜欢这个。我还没有在任何 Bash 文档中找到它,所以这确实是一个隐藏功能。
【讨论】:
C 风格的数字表达式:
let x="RANDOM%2**8"
echo -n "$x = 0b"
for ((i=8; i>=0; i--)); do
let n="2**i"
if (( (x&n) == n )); then echo -n "1"
else echo -n "0"
fi
done
echo ""
【讨论】:
这些属性是我最喜欢的另一个。
export HISTCONTROL=erasedups
export HISTSIZE=1000
第一个确保 bash 不会多次记录命令,这将真正提高 history 的实用性。另一个将历史记录大小从默认的 100 扩展到 1000。我实际上在我的机器上将它设置为 10000。
【讨论】: