对于管理员或用户而言,命令行不仅是可以完成所有任务的工具,而且还是可以永远开发的高度定制的工具。
最近,有一篇有关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而不是grep和cut,因此执行时间会更少。
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}
结果
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
因此,您只需忘记睡眠命令即可。
我怀疑并不是所有的花招看起来都很有趣,但是在我看来,有十二个数字是可以填写的好数字。