Linux shell的许多技巧可以节省您的时间



  • 首先,您可以在这里阅读俄语的这篇文章。

一天晚上,我正在阅读Jeffrey Friedl的Mastering正则表达式 ,我意识到即使您拥有所有文档和丰富的经验,也可能会有很多由不同的人开发并被囚禁的技巧。 所有人都不一样。 对某些人来说显而易见的技术对其他人可能并不明显,而对于第三人称来说却像是某种怪异的魔术。 顺便说一句,我已经在这里用俄语描述了一些这样的时刻。

对于管理员或用户而言,命令行不仅是可以完成所有任务的工具,而且还是可以永远开发的高度定制的工具。 最近,有一篇有关CLI中一些有用技巧的翻译文章。 但是我觉得翻译人员没有足够的CLI经验,也没有遵循所描述的技巧,因此许多重要的事情可能会被遗漏或误解。

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

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

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


人们经常使用cut甚至awk只是通过模式或使用分隔符减去字符串的一部分。
另外,许多人使用$ {VARIABLE:start_position:length}的子字符串bash操作,其运行速度非常快。

但是bash提供了一种使用#,##,%和%%处理文本字符串的有效方法-这称为bash变量扩展
使用此语法,您可以在无需执行外部命令的情况下减少模式的需要,因此它将非常快速地工作。

下面的示例显示如何从字符串中获取第三列(shell),其中使用cut或使用变量扩展名(用*: 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 
结果
 $ ./test.sh $ ./test.sh using cut real 0m0.827s user 0m0.004s sys 0m0.208s using ## real 0m0.613s user 0m0.436s sys 0m0.172s 
没有评论=)

还有更多示例:

提取等号后的值:

 $ 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 

它是可选的
通常默认情况下启用它,但是您可以在以下变量中配置此行为:

导出HISTCONTROL =忽略两者


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,PCRE甚至不知道分组。
-i忽略大小写。
--line-buffered立即解析行,而不是等待到达标准的4k缓冲区(对于tail -f | grep非常有用)。

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

 $ 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. 手表在看着你!


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

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

 watch <command> 

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

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

8.打击顺序


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

 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} 

结果
 project/ !--- src/ |--- main/ | |-- java/ | !-- resources/ !--- test/ |-- java/ !-- resources/ 

9.尾巴,多个文件,多个用户...


我已经提到了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.睡觉吗? 一会儿 阅读!


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

 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 

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

我怀疑并不是所有的技巧看起来都很有趣,但是在我看来,有十二个数字是可以填写的好数字。

在此,我说再见,感谢您参加调查。

当然可以随意讨论以上内容,并在评论中分享您的绝妙招!

Source: https://habr.com/ru/post/zh-CN444890/


All Articles