Shell 脚本通常用作粘合剂,用于自动化和简单的一次性任务。您最喜欢 Bash shell/脚本语言的哪些“隐藏”功能?
- 每个答案一个功能
- 提供功能的示例和简短描述,而不仅仅是文档链接
- 使用粗体标题作为第一行标记特征
也可以看看:
Shell 脚本通常用作粘合剂,用于自动化和简单的一次性任务。您最喜欢 Bash shell/脚本语言的哪些“隐藏”功能?
也可以看看:
insert preceding line's final parameter
alt-. the most useful key combination ever, try it and see, for some reason no one knows about this one.
press it again and again to select older last parameters.
great when you want to do something else to something you used just a moment ago.
If you want to keep a process running after you log out:
disown -h <pid>
is a useful bash built-in. Unlike nohup
, you can run disown
on an already-running process.
First, stop your job with control-Z, get the pid from ps
(or use echo $!
), use bg
to send it to the background, then use disown
with the -h flag.
Don't forget to background your job or it will be killed when you logout.
手册中 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+* 插入所有可能的补全,这特别有用,例如,如果您刚刚输入了带有通配符的潜在破坏性命令:
rm -r source/d*.c
Alt+*
rm -r source/delete_me.c source/do_not_delete_me.c
Ctrl++在当前行上执行别名、历史和 shell 扩展Alt。e换句话说,当前行将被重新显示,因为它将由 shell 处理:
ls $HOME/tmp
Ctrl Alt+e
ls -N --color=tty -T 0 /home/cramey
取回历史命令和参数
可以使用!
运算符有选择地访问以前的命令和参数。当您使用长路径时,它非常有用。
您可以使用 . 检查您的最后命令history
。
您可以使用以前的命令!<n>
作为n
中命令的索引history
,负数从历史中的最后一个命令开始倒数。
ls -l foo bar
touch foo bar
!-2
您可以使用前面的参数!:<n>
,零是命令,>= 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
特殊变量随机:
if [[ $(($RANDOM % 6)) = 0 ]]
then echo "BANG"
else
echo "Try again"
fi
拼写错误的快速和肮脏更正(特别适用于慢速连接上的长命令,其中使用命令历史记录并滚动浏览它会很糟糕):
$ cat /proc/cupinfo
cat: /proc/cupinfo: No such file or directory
$ ^cup^cpu
还可以尝试!:s/old/new
在上一个命令中将旧的替换为新的。
如果要替换多次出现,可以使用!:gs/old/new
.
您可以将gs
ands
命令与任何历史事件一起使用,例如
!-2:s/old/new
old
在倒数第二个命令中用new
(一次)替换。
Ctrlx Ctrle
这会将当前命令加载到变量 VISUAL 中定义的编辑器中。这对于像这里列出的一些长命令非常有用。
使用 vi 作为编辑器:
export VISUAL=vi
正则表达式处理
最近的 bash 版本具有正则表达式匹配功能,因此您可以执行以下操作:
if [[ "mystring" =~ REGEX ]] ; then
echo match
fi
其中 REGEX 是格式由 man re_format 描述的原始正则表达式。
任何括号部分的匹配都存储在 BASH_REMATCH 数组中,从元素 1 开始(元素 0 是整个匹配的字符串),因此您也可以使用它来进行正则表达式驱动的解析。
这是我最喜欢的两个:
要检查没有真正执行脚本的语法,请使用:
bash -n script.sh
返回上一个目录(是的,我知道 pushd 和 popd,但这更快)
cd -
在显示 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 中,然后坐下来观看混乱的展开。
数组:
#!/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 脚本编写内容)的更多详细信息。
man
来自 bash页面的魔法组合键: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
页面中缩进。
使用中缀布尔运算符
考虑简单的 if:
if [ 2 -lt 3 ]
then echo "Numbers are still good!"
fi
-lt 看起来有点难看。不是很现代。如果您在布尔表达式周围使用双括号,则可以使用普通的布尔运算符!
if [[ 2 < 3 ]]
then echo "Numbers are still good!"
fi
export TMOUT=$((15*60))
在 15 分钟空闲时间后终止 bash,设置为 0 以禁用。我通常把它放在我的根帐户上的 ~/.bashrc 中。它在管理您的盒子时很方便,您可能会在离开终端之前忘记注销。
C-S-- 控制 Shift 减去撤消键入操作。
任何删除操作C-w(删除前一个单词),C-k(删除到行尾),C-u(删除到行首)等...将其删除的文本复制到终止环,您可以粘贴最后一个终止:C-y并循环(和粘贴来自)已删除项目的环Alt-y
FIGNORE
您可以通过设置 th变量在选项卡完成时忽略某些文件。
例如,如果您有一个 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
截断文件的内容(归零文件)
> file
具体来说,这对于截断日志文件非常有用,当文件被另一个进程打开时,仍然可能写入文件。
我最近阅读了Csh Programming Considered Harmful,其中包含这个惊人的宝石:
考虑管道:
A | B | C
你想知道 C 的状态,嗯,很简单:它在 $? 中,或者在 csh 中的 $status。但是如果你想从 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;
不是一个真正的功能,而是一个方向:我在commandlinefu.com发现了许多“隐藏的功能”、秘密和各种 bash 有用性。这个答案的许多评价最高的答案,我在那个网站上学到了:)
另一个小的: Alt+#
注释掉当前行并将其移动到历史缓冲区中。
因此,当您组装命令行并且需要发出临时命令来查找文件时,您只需按 alt+#,发出另一个命令,进入历史记录,取消注释并继续。
大括号代替do
和done
for 循环
For
循环体通常在do...done
(只是一个例子):
for f in *;
do
ls "$f";
done
但是我们可以使用大括号来使用 C 风格:
for f in *; {
ls "$f";
}
我认为这看起来比do...done
我更喜欢这个。我还没有在任何 Bash 文档中找到这个,所以这确实是一个隐藏的特性。
These properties are another one of my favorites.
export HISTCONTROL=erasedups
export HISTSIZE=1000
The first one makes sure bash doesn't log commands more than once, will really improves history
's usefulness. The other expands the history size to 1000 from the default of 100. I actually set this to 10000 on my machines.
在多个目录之间轻松移动
不是隐藏功能,但比需要类似堆栈导航的 pushd 灵活得多。
a() { alias $1=cd\ $PWD; }
cd
某处并输入a 1
。稍后只需键入1
将返回该目录。
我经常使用的一个是 !$ 来引用最后一个命令的最后一个单词:
$ less foobar.txt
...
# I dont want that file any more
$ rm !$
set -o vi
为了对命令历史以及当前键入的命令进行类似 vi 的编辑。
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 ""
我有一个 alias r='fc-s'
,我发现它在某些有限的情况下非常有用。要运行最后一个命令,只需键入r
并按 Enter,就可以了。当然,这本身并不是很有用,因为向上箭头做同样的事情。但是您可以使用r
替换来运行上一个命令。假设您的最后一个命令是编译某个文件的长命令:
$ gcc -c <file_name>.c <lots of options> -o <file_name>.o
现在你想用相同的选项编译另一个文件并有一个相应的.o
文件:
$ r <file_name>=<new_file>
会做的。您不必使用向上箭头,导航到正确的位置,然后手动替换它们。这可以重复多次,因此您可以接下来执行此操作:
$ r <new_file>=<other_file>
当然,对于这样的事情,你有 makefiles,但我希望我已经证明了别名是有用的。
我不需要使用这个别名很多,但有时我很高兴我有这个别名!
正如其他人所提到的,Ctrl-r非常适合回顾您的命令历史记录。但是,如果您在迈出太多一步或几步之后还想继续前进怎么办?这就是Ctrl-s派上用场的地方。但是,它通常映射到 XOFF(中断数据流)。由于我们没有使用慢速串行终端,所以这不再有用了,您可以使用以下命令关闭该映射:
stty -ixon
在你的~/.bashrc
文件中。
这也使得Ctrl-q可用,它通常是Ctrl-的副本v(带引号的插入允许您插入文字控制字符)。我已经Ctrl-q映射到菜单完成,重复按下时会逐步完成。我喜欢离开Tab设置为常规完成。
您可以通过将此行添加到文件中来将Ctrl-设置为菜单完成:q~/.inputrc
"\C-q": menu-complete
Bash 有可变间接:
$ foo=bar
$ baz=foo
$ echo ${!baz}
bar
这里是字符串( <<<
)。Bash 手册给出了这样的描述:
该词被扩展并提供给其标准输入上的命令。
例子:
$ cat<<<"$(( 10*3+1 )) nice isn't it?"
31 nice isn't it?
特殊套接字文件名:/dev/tcp/HOST/PORT 和 /dev/udp/HOST/PORT
从日间服务器(端口 13)读取:
$ cat < /dev/tcp/utcnist.colorado.edu/13
55786 11-08-13 03:34:21 50 0 0 172.3 UTC(NIST) *
这与tcpserver结合使用非常有用。
如果您无权访问 wget 或 curl,则来自http://thesmithfam.org/blog/2006/05/23/bash-socket-programming-with-devtcp-2/的更高级示例:
$ exec 3<>/dev/tcp/www.google.com/80 # hook up to file desc 3
$ echo -e "GET / HTTP/1.1\n\n" >&3 # send the HTTP request
$ cat <&3 # read the HTTP response
使用 'let' 内置 bash 命令进行基本算术运算
A=10
let B="A * 10 + 1" # B=101
let B="B / 8" # B=12, let does not do floating point
let B="(RANDOM % 6) + 1" # B is now a random number between 1 and 6
要进行浮点计算,您可以使用“bc”命令(不是 bash 的一部分)。
FP=`echo "scale=4; 10 / 3" | bc` # FP="3.3333"
使用 <(cmd ...) 或 >(cmd ...) 进行进程替换
在每种形式中,cmd 都会在其输入或输出连接到 FIFO 的情况下执行,并且该 FIFO 的路径在命令行中被替换:
$ echo A file to read: <(cat), a file to write to: >(cat)
A file to read: /dev/fd/63, a file to write to: /dev/fd/62
例如,在不保存中间文件的情况下比较两个网站:
$ diff <(curl -s http://tldp.org/LDP/abs/html/) <(curl -s http://www.redhat.com/mirrors/LDP/LDP/abs/html/)
如果您有一个将文件名作为输入的命令,但不接受“-”表示标准输出,您可以欺骗它:
$ do_thingee --log -
error: can't open log file: '-'
$ do_thingee --log >(cat)
do_thingee v0.2
initializing things
processing 4 things
done
嵌入式命令替换:
主机名 && dig +short $(hostname) && dig +short -x $(dig +short $(hostname))
此命令适用于检查邮件服务器上的 RDNS。:P
在http://linuxconfig.net/manual-howto/key-combinations-in-bash.html中获取有关 Bash 中的组合键的更多信息
快速历史搜索
以下提供了一个类似 tcsh 的历史搜索,它既方便又容易。
将以下行添加到~/.inputrc
或/etc/inputrc
。
$ cat ~/.inputrc
"\e[A": history-search-backward
"\e[B": history-search-forward
您可能希望使用不太意外的组合键,例如Esc
+ p
。如果是这种情况,请使用
"\ep": history-search-backward
"\en": history-search-forward
然后,只需键入前几个字母并按向上箭头键。它将显示以给定字母开头的最新命令。
前任:
类型grep
,向上箭头。它会显示类似grep -ri myText .