62

加薪,首先要高效,Linux shell的12个巧妙技巧让你运维高效

 4 years ago
source link: https://www.tuicool.com/articles/Mjm2yiB
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ry2E3uM.jpg!web

对于管理员或用户而言,命令行不仅是可以完成所有任务的工具,而且还是可以永远开发的高度定制的工具。

最近,有一篇有关CLI中一些有用技巧的翻译文章。但是我觉得翻译人员没有足够的CLI经验,也没有遵循所描述的技巧,因此许多重要的事情可能会被遗漏或误解。

根据个人经验,在Linux Shell中有12种技巧。

注意:本文中的所有脚本和示例都经过了尽可能地简化,因此也许您会发现一些看起来完全没用的技巧–也许就是这个原因。但无论如何,请在评论中分享您的想法!

1.使用可变扩展名分割字符串

人们经常使用 cut 甚至 awk 只是通过模式或使用分隔符减去字符串的一部分。

另外,许多人使用 $ {VARIABLE:start_position:length} 进行子字符串bash操作,这非常快。

但是bash提供了一种使用#,##,%和%%来处理文本字符串的强大方法-它称为 bash变量扩展

使用此语法,您可以在无需执行外部命令的情况下减少模式的需要,因此它将非常快速地工作。

下面的示例显示了如何使用 cut 或变量扩展从字符串中获取第三列(shell),其中用冒号«username:homedir:shell»分隔的值(我们使用 *: mask和##命令,这意味着:将所有字符向左剪切,直到找到最后一个冒号为止):

$ STRING="username:homedir:shell" 
$ echo "$STRING"|cut -d ":" -f 3 
shell 
$ echo "${STRING##*:}" 
shell 

第二个选项不启动子进程( cut ),并且根本不使用管道,这样可以更快地工作。而且,如果您在管道几乎不移动的Windows上使用bash子系统,则速度差异会 很大

让我们看一下Ubuntu上的示例:循环执行我们的命令1000次

$ cat test.sh 
#!/usr/bin/env bash 
STRING="Name:Date:Shell" 
echo "using cut" 
time for A in {1..1000} 
do 
 cut -d ":" -f 3 > /dev/null <<<"$STRING" 
done 
echo "using ##" 
time for A in {1..1000} 
do 
 echo "${STRING##*:}" > /dev/null 
done 

结果

$ ./test.sh 
using cut 
real 0m0.950s 
user 0m0.012s 
sys 0m0.232s 
using ## 
real 0m0.011s 
user 0m0.008s 
sys 0m0.004s 

差别是几十倍!

当然,上面的例子太人为了。在实际示例中,我们将不使用静态字符串,而是要读取真实文件。对于“ cut ”命令,我们只将/etc /passwd重定向到它。在##的情况下,我们必须创建一个循环并使用内部的' read '命令读取文件。那么谁将赢得这场案子呢?

$ cat test.sh 
#!/usr/bin/env bash 
echo "using cut" 
time for count in {1..1000} 
do 
 cut -d ":" -f 7 </etc/passwd > /dev/null 
done 
echo "using ##" 
time for count in {1..1000} 
do 
 while read 
 do 
 echo "${REPLY##*:}" > /dev/null 
 done </etc/passwd 
done 

结果

还有两个示例:

在等号后提取值:

$ VAR="myClassName = helloClass" 
$ echo ${VAR##*= } 
helloClass 

提取括号中的文本:

$ VAR="Hello my friend (enemy)" 
$ TEMP="${VAR##*\(}" 
$ echo "${TEMP%\)}" 
enemy 

2. Bash自动补全

bash-completion软件包几乎是每个Linux发行版的一部分。您可以在/etc/bash.bashrc或/etc/profile.d/bash_completion.sh中启用它,但是通常默认情况下已启用它。通常,自动完成是新手首先遇到的Linux Shell上的第一个便捷时刻。

但是并非所有人都使用所有bash补全功能这一事实,在我看来完全是徒劳的。例如,不是所有人都知道,自动完成功能不仅适用于文件名,而且适用于别名,变量名,函数名,甚至适用于某些带有参数的命令。如果您深入研究自动完成脚本(实际上是shell脚本),甚至可以为自己的应用程序或脚本添加自动完成。

但是,让我们回到别名。

您无需编辑PATH变量或在指定目录中创建文件即可运行别名。您只需要将它们添加到配置文件或启动脚本中,然后在任何位置执行它们即可。

通常,我们在* nix中使用小写字母表示文件和目录,因此创建大写别名非常方便-在这种情况下,bash-completion 几乎会用单个字母来猜测您的命令:

$ alias TAsteriskLog="tail -f /var/log/asteriks.log" 
$ alias TMailLog="tail -f /var/log/mail.log" 
$ TA[tab]steriksLog 
$ TM[tab]ailLog 

3.使用选项卡进行Bash自动补全-第2部分

对于更复杂的情况,可能您想将个人脚本放入$ HOME / bin。

但是我们在bash中有功能。

函数不需要路径或单独的文件。(注意)bash补全也可以与函数一起使用。

让我们在 .profile中 创建函数LastLogin (不要忘记重新加载.profile):

function LastLogin { 
 STRING=$(last | head -n 1 | tr -s " " " ") 
 USER=$(echo "$STRING"|cut -d " " -f 1) 
 IP=$(echo "$STRING"|cut -d " " -f 3) 
 SHELL=$( grep "$USER" /etc/passwd | cut -d ":" -f 7) 
 echo "User: $USER, IP: $IP, SHELL=$SHELL" 
} 

在控制台中(请注意,函数名的首字母大写以加快bash的完成速度):

$ L[tab]astLogin 
User: saboteur, IP: 10.0.2.2, SHELL=/bin/bash 

4.1.敏感数据

如果您在控制台中的任何命令前放置空格,则它将不会出现在命令历史记录中,因此,如果您需要在命令中放置纯文本密码,这是使用此功能的一种好方法—在下面的示例中 回显«hello 2» 将不会出现在历史记录中:

$ echo "hello" 
hello 
$ history 2 
 2011 echo "hello" 
 2012 history 2 
$ echo "my password secretmegakey" # there are two spaces before 'echo' 
my password secretmegakey 
$ history 2 
 2011 echo "hello" 
 2012 history 2 

它是可选的

4.2.命令行参数中的敏感数据

您想在git中存储一些shell脚本以在服务器之间共享它们,或者它可能是应用程序启动脚本的一部分。并且您希望此脚本将连接到数据库或执行其他需要凭据的操作。

当然,将凭据存储在脚本本身中是个坏主意,因为git是不安全的。

通常,您可以使用已经在目标环境上定义的变量,并且脚本本身将不包含密码。

例如,您可以在具有700个权限的每个环境上创建小脚本,并使用主脚本中的 source 命令调用它:

secret.sh 
PASSWORD=LOVESEXGOD 
myapp.sh 
source ~/secret.sh 
sqlplus -l user/"$PASSWORD"@database:port/sid @mysqfile.sql 

但这并不安全。

如果其他人可以登录到您的主机,则他只需执行 ps 命令并查看带有整个命令行参数(包括密码)的sqlplus进程。因此,安全工具通常应该能够直接从文件中读取密码/密钥/敏感数据。

例如,安全 ssh 甚至没有任何选项可以在命令行中提供密码。但是他可以从文件读取ssh密钥(并且可以在ssh密钥文件上设置安全权限)。

非安全wget具有选项“ --password”,该选项使您可以在命令行中提供密码。wget一直在运行,每个人都可以执行ps命令并查看您提供的密码。

另外,如果您有很多敏感数据,并且想通过git控制它,那么唯一的方法就是加密。因此,您只需将每个主密码以及所有其他可以加密并放入git的数据输入到每个目标环境。而且,您可以使用openssl CLI界面从命令行使用加密的数据。以下是从命令行进行加密和解密的示例:

文件secret.key包含主密钥-单行:

$ echo "secretpassword" > secret.key; chmod 600 secret.key 

让我们使用aes-256-cbc加密字符串:

$ echo "string_to_encrypt" | openssl enc -pass file:secret.key -e -aes-256-cbc -a 
U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML 

您可以将此加密的字符串放入git或其他任何位置存储的任何配置文件中-没有secret.key,几乎不可能对其进行解密。

要解密执行同一命令,只需将-e替换为-d即可:

$ echo 'U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML' | openssl enc -pass file:secret.key -d -aes-256-cbc -a 
string_to_encrypt 

5. grep命令

所有人都应该知道grep命令。并且对正则表达式要友好。通常,您可以编写如下内容:

tail -f application.log | grep -i error 

甚至像这样:

tail -f application.log | grep -i -P "(error|warning|failure)" 

但是不要忘记grep有很多很棒的选择。例如-v,它会还原您的搜索并显示除“ info”消息以外的所有消息:

tail -f application.log | grep -v -i "info" 

其他内容:

选项-P非常有用,因为默认情况下,grep使用相当过时的«基本正则表达式:»,并且-P启用PCRE,甚至不知道分组。

-i忽略大小写。

--line-buffered立即解析行,而不是等待到达标准的4k缓冲区(对于tail -f | grep非常有用)。

如果您非常了解正则表达式,则使用--only-matching / -o可以真正实现剪切文本的出色功能。只需比较以下两个命令以提取myuser的shell:

$ grep myuser /etc/passwd| cut -d ":" -f 7 
$ grep -Po "^myuser(:.*){5}:\K.*" /etc/passwd 

第二个命令看起来更编译,但是它只运行 grep 而不是 grepcut ,因此执行时间会更少。

6.如何减少日志文件的大小

在* nix中,如果删除应用程序当前正在使用的日志文件,则不能仅删除所有日志,还可以阻止应用程序在重新启动之前编写新日志。

由于文件描述符不是打开文件名而是打开iNode结构,因此应用程序将继续将文件描述符写入没有目录条目的文件,并且该文件将在应用程序停止后由文件系统自动删除( 您的应用程序可以每次想写一些东西来避免这种问题时都要打开和关闭日志文件,但这会影响性能 )。

因此,如何清除日志文件而不删除它:

echo "" > application.log 

或者我们可以使用truncate命令:

truncate --size=1M application.log 

提及,该 truncate 命令将删除文件的其余部分,因此您将丢失最新的日志事件。另一个示例如何存储最后1000行:

echo "$(tail -n 1000 application.log)" > application.log 

PS在Linux中,我们有标准的服务rotatelog。您可以将日志添加到自动截断/旋转中,也可以使用现有的日志库来完成(例如Java中的log4j)。

7.watch

在某些情况下,您正在等待事件结束。例如,当另一个用户登录到shell(您连续执行 who 命令)时,或者某人应该使用scp或ftp将文件复制到您的计算机上时,您正在等待完成(重复ls数十次)。

在这种情况下,您可以使用

watch <command> 

默认情况下,将每隔2秒钟执行一次,且屏幕会预先清除,直到按Ctrl + C。您可以配置执行频率。

当您要观看实时日志时,此功能非常有用。

8.bash顺序

创建范围非常有用。例如,而不是像这样:

for srv in 1 2 3 4 5; do echo "server${srv}";done 
server1 
server2 
server3 
server4 
server5 

您可以编写以下内容:

for srv in server{1..5}; do echo "$srv";done 
server1 
server2 
server3 
server4 
server5 

您也可以使用 seq 命令生成格式化范围。例如,我们可以使用 seq 创建值,将根据宽度(00、01而不是0、1)自动调整抽动:

for srv in $(seq -w 5 10); do echo "server${srv}";done 
server05 
server06 
server07 
server08 
server09 
server10 

使用命令替换的另一个示例-重命名文件。要获取不带扩展名的文件名,我们使用“ basename ”命令:

for file in *.txt; do name=$(basename "$file" .txt);mv $name{.txt,.lst}; done 

甚至还比'%'更短:

for file in *.txt; do mv ${file%.txt}{.txt,.lst}; done 

PS实际上,对于重命名文件,您可以尝试使用具有许多选项的“ 重命名 ”工具。

另一个示例-让我们为新的Java项目创建结构:

mkdir -p project/src/{main,test}/{java,resources} 

结果

7reaemu.jpg!web

9.tail, multiple files, multiple users...

我已经提到了 multitail 来读取文件并观看多个实时日志。但是默认情况下未提供该功能,并且安装某些内容的权限并非始终可用。

但是标准尾巴也可以做到:

tail -f /var/logs/*.log 

还让您记住有关用户的信息,这些用户使用'tail -f'别名查看应用程序日志。

多个用户可以使用“ tail -f”同时观看日志文件。他们中有些人的会话不太准确。由于某种原因,他们可能会将'tail -f'留在背景中而忘记了。

如果重新启动应用程序,则有一些正在运行的“ tail -f”进程正在监视不存在的日志文件,该进程可能会挂起几天甚至几个月。

通常这不是一个大问题,但不是整齐的。

如果您使用别名来查看日志,则可以使用--pid选项修改此别名:

alias TFapplog='tail -f --pid=$(cat /opt/app/tmp/app.pid) /opt/app/logs/app.log' 

在这种情况下,重新启动目标应用程序时,所有 尾部 将自动终止。

10.创建具有指定大小的文件

dd是使用块和二进制数据的最受欢迎的工具之一。例如,创建1 MB文件并填充零将是:

dd if=/dev/zero of=out.txt bs=1M count=10 

但我建议使用 fallocate

fallocate -l 10M file.txt 

在支持分配功能(xfs,ext4,Btrfs ...)的文件系统上, fallocate 将立即执行,这与dd工具不同。另外,分配是指实际分配块,而不是创建备用文件。

11. xargs

很多人都知道流行的 xargs 命令。但是并非所有人都使用以下两个选项,因此可以极大地改善脚本。

首先-您可以获得非常长的参数列表,并且可能超过命令行长度(默认情况下〜4 kb)。

但是您可以使用-n选项限制执行,因此 xargs 将多次运行命令,一次发送指定数量的参数:

$ # lets print 5 arguments and send them to echo with xargs:  
$ echo 1 2 3 4 5 | xargs echo 
1 2 3 4 5 
$ # now let’s repeat, but limit argument processing by 3 per execution 
$ echo 1 2 3 4 5 | xargs -n 3 echo 
1 2 3 
4 5 

来吧 处理长列表可能需要很多时间,因为它在单个线程中运行。但是,如果我们有几个核心,我们可以告诉 xargs 并行运行:

echo 1 2 3 4 5 6 7 8 9 10| xargs -n 2 -P 3 echo 

在上面的示例中,我们告诉 xargs 处理3个线程中的list;每个线程每次执行将接受并处理2个参数。如果您不知道自己有多少个内核,请使用“ nproc ” 进行优化:

echo 1 2 3 4 5 6 7 8 9 10 | xargs -n 2 -P $(nproc) echo 

12.sleep? while? read!

有时您需要等待几秒钟。或等待用户输入以下内容:

read -p "Press any key to continue " -n 1 

但是您只需添加超时选项以 读取 命令,脚本就会暂停指定的秒数,但是在交互执行的情况下,用户可以轻松地跳过等待。

read -p "Press any key to continue (auto continue in 30 seconds) " -t 30 -n 1 

因此,您只需忘记睡眠命令即可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK