有关bash,我但愿我能知晓的十件事

简介

我以前的一篇文章比我预想的更受欢迎,所以我想再写一篇文章来介绍一些不太知名的bash功能python

正如以前所言,因为我以为bash是一种要常用(且需理解)的技术,因此我在研究bash时写了一本书。虽然许多人并不熟悉bash,但我以为他们也认为很是重要便足够使人欣喜。git

1)^x^y^

我总在使用的一个小技巧。shell

历来没有输入过相似的命令?编程

$ grp somestring somefile
-bash: grp: command not found

哎,这个命令敲错了,因此你要敲“↑”,而后敲”←“直到”p“,而后输入”e"再执行。数组

或者这样输入:bash

$ ^rp^rep^
grep 'somestring' somefile
$

你可能须要注意的一个细节是:app

$ grp rp somefile
$ ^rp^rep^
$ grep rp somefile

若是你想搜索“rep”,那你就要深刻研究man page,学会使用这个更强大的命令:编程语言

$ grp rp somefile
$ !!:gs/rp/rep
grep rep somefile
$

我不会在这里解释这个用法。。。ide

2)pushd/popd

这个在脚本中很是好用,特别是在循环中函数

以下所示,假设你正在写一个进入退出文件夹的for循环:

for d1 in $(ls -d */)
do
 # Store original working directory.
 original_wd="$(pwd)"
 cd "$d1"
 for d2 in $(ls -d */)
 do
   pushd "$d2"
   # Do something
   popd
 done
 # Return to original working directory
 cd "${original_wd}"
done

你能够像这样使用pushd栈来重写上方代码:

for d1 in $(ls -d *)
do
 pushd "$d1"
 for d2 in $(ls  -d */)
 do
   pushd "$d2"
   # Do something
   popd
 done
 popd
done

它能够追踪记录你切换的目录并进行入栈或出栈

注意,当使用pushd出现错误时,可能会丢失栈的记录而且popd屡次。所以你可能会想要在脚本中使用set -e(见上一篇文章)

固然也能够用cd -,可是它不会使用栈——仅仅返回前一个目录

cd ~
cd /tmp
cd blah
cd - # Back to /tmp
cd - # Back to 'blah'
cd - # Back to /tmp
cd - # Back to 'blah' ...

3) shopt vs set

这两个命令困扰了我一阵子。

二者之间有什么不一样呢?

set在以前的文章已经介绍过了,而shopt看起来与之类似。只输入shopt会显示一系列选项:

$ shopt
cdable_vars    off
cdspell        on
checkhash      off
checkwinsize   on
cmdhist        on
compat31       off
dotglob        off

我在这里( here)找到了一些答案。

从根本上说,彷佛有一系列的bash(和其余shells)创建在sh之上,而添加shopt命令则为设置额外的shell选项提供了一种方式

可是我也不肯定……若是你知道为何,请告诉我。

4)Here Docs 与 Here Strings

“Here Docs”是在shell中用一些语句建立的文件。

“诀窍”很简单。定义一个用于结束的单词,则在这个单词单独出如今一行以前的全部输入行将构成文件。

像这样:

$ cat > afile << SOMEENDSTRING
> here is a doc
> it has three lines
> SOMEENDSTRING alone on a line will save the doc
> SOMEENDSTRING
$ cat afile
here is a doc
it has three lines
SOMEENDSTRING alone on a line will save the doc
$

注意:

· 若是结束单词不是“单独”出如今一行中,那它能够构成文件

· SOMEENDSTRING一般是END,但这仅仅只是习惯

更不为人知的是“here string”:

$ cat > asd <<< 'This file has one line'

5)字符串变量的操做

之前你多是像下面展现的那样写代码,用sed一类的工具来操做字符串:

$ VAR='HEADERMy voice is my passwordFOOTER'
$ PASS="$(echo $VAR | sed 's/^HEADER(.*)FOOTER/1/')"
$ echo $PASS

可是你可能不知道bash自己也是能够的。

这意味着你能够省去大量的sed和awk。

一种重写上述代码的方式以下所示:

$ VAR='HEADERMy voice is my passwordFOOTER'
$ PASS="${VAR#HEADER}"
$ PASS="${PASS%FOOTER}"
$ echo $PASS

·#表示“从字符串开头开始匹配并删除所给的模式串”

·%表示“从字符串结尾开始匹配并删除所给的模式串”

在个人电脑上,后一种方法比前一种快两倍。而且(令我吃惊的是),他的速度跟相似功能的python脚本速度大体至关

若是你想使用通配符(见前文)模式串并采用贪婪模式,你须要双写:

$ VAR='HEADERMy voice is my passwordFOOTER'
$ echo ${VAR##HEADER*}

$ echo ${VAR%%*FOOTER}

6)变量的默认值

这些对写脚原本说很是好用。

若是你有一个没有赋值的变量,你能够像这样给它“赋默认值”

建立一个default.sh文件,写入以下内容:

#!/bin/bash
FIRST_ARG="${1:-no_first_arg}"
SECOND_ARG="${2:-no_second_arg}"
THIRD_ARG="${3:-no_third_arg}"
echo ${FIRST_ARG}
echo ${SECOND_ARG}
echo ${THIRD_ARG}

如今执行chmod +x default.sh并用./default.sh first second来运行脚本:

观察第三个参数的默认值是如何被分配的,而不是前两个。

你也能够直接用${VAR:=defaultval}(等号,不是破折号),可是注意这不适用于脚本或函数中的位置变量。尝试修改上面的脚原本看它是如何失败的。

7)Traps

当一个信号被送到脚本时,内建的trap能够用于“捕获”

下面是我用在本身的chepci脚本中的一个例子:

function cleanup() {
   rm -rf "${BUILD_DIR}"
   rm -f "${LOCK_FILE}"
   # get rid of /tmp detritus, leaving anything accessed 2 days ago+
   find "${BUILD_DIR_BASE}"/* -type d -atime +1 | rm -rf
   echo "cleanup done"                                                                                                                          
} 
trap cleanup TERM INT QUIT

任何使用TERM信号的CTRL-C,CTRL-或终止程序的操做将会首先调用cleanup

注意:

·trap的逻辑可能很是棘手(例如处理信号竞争条件)

·KILL信号不能以这种方式捕获

可是大多数状况下,我会把它用于相似上述的‘cleanup’中,来达成函数的目的。

8)Shell变量

了解可用的标准shell变量是很是值得的。这些是我最喜欢的。

RANDOM

不要依赖这个来加密堆栈,但你能够生成随机数字,例如在脚本中建立临时文件时:

$ echo ${RANDOM}
16313
$ # Not enough digits?
$ echo ${RANDOM}${RANDOM}
113610703
$ NEWFILE=/tmp/newfile_${RANDOM}
$ touch $NEWFILE

REPLY

不在须要给read一个变量名称

$ read
my input
$ echo ${REPLY}

LINENO 与 SECONDS

方便调试

$ echo ${LINENO}
115
$ echo ${SECONDS}; sleep 1; echo ${SECONDS}; echo $LINENO
174380
174381
116

注意,即使使用;来隔开命令,上面的代码也要分两行

TMOUT

能够用来超时读取,在一些脚本中真的很好用

#!/bin/bash
TMOUT=5
echo You have 5 seconds to respond...
read
echo ${REPLY:-noreply}

9) Extglobs

若是你真的沉迷bash不能自拔,那么你可能想要加强你的通配功能。你能够经过设置shell中的extglob选项。这是设置方法:

shopt -s extglob
A="12345678901234567890"
B="  ${A}  "

如今来看看你是否能指出如下这些语句各自的功能:

echo "B      |${B}|"
echo "B#+( ) |${B#+( )}|"
echo "B#?( ) |${B#?( )}|"
echo "B#*( ) |${B#*( )}|"
echo "B##+( )|${B##+( )}|"
echo "B##*( )|${B##*( )}|"
echo "B##?( )|${B##?( )}|"

虽然它可能颇有用,可是很难想象出一种你必需要用这种方式的状况。一般你会使用一些更适合相应任务的工具(像sed)或者直接放弃bash去使用一些像python那样的“合适的”编程语言。

10)关联数组

谈到移植到其余语言,一条重要的规则是,若是我须要用到数组,那么我会放弃bash,使用python(为此我甚至建立了一个Docker Container来运行一个专门的工具)

知道读到它我才知道,在bash中有关联数组

如下是演示:

$ declare -A MYAA=([one]=1 [two]=2 [three]=3)
$ MYAA[one]="1"
$ MYAA[two]="2"
$ echo $MYAA
$ echo ${MYAA[one]}
$ MYAA[one]="1"
$ WANT=two
$ echo ${MYAA[$WANT]}

注意仅适用于bash4.x+版本

本文由马哥教育Linux学院提供支持。

相关文章
相关标签/搜索