假设您的历史记录文件中有类似于以下的shell历史记录:
find . -type f | x grep 'linux' | wc -l
注意:x
是xargs
命令的别名。
我想写一个shell函数,它将上面的文本作为参数并返回使用的命令的完整路径。
此文本的返回值应该是命令路径数组:("/usr/bin/find" "/usr/bin/xargs" "/usr/bin/wc")
如果我将"whereis where"
作为参数提供给函数,where
是一个内置的shell,因此没有它的路径。函数应返回类似于("/usr/bin/where", "where")
。
我认为我可以通过对文本应用常规过期来做到这一点,但我知道正则表达式较少而且对awk
不太熟悉。
更新
输入和输出示例:
$ exctractCommands "find . -type f | x grep 'linux' | wc -l; where ls"
/usr/bin/find
/usr/bin/xargs
/usr/bin/wc
where
请问如何编写此功能?
答案 0 :(得分:1)
您希望输出看起来有点混淆,但应该很容易从下面的脚本更改(如果您只需要命令路径/别名,只需将out="$i"
更改为out=""
)。请注意,bash在处理shell脚本中的别名方面并不是特别擅长,因此您必须提供保存它们的任何文件。
#!/bin/bash
ali() {
arg="$*"
input=$(echo "$arg"| tr ' ' '\n')
save=""
while read i; do
out=$(type "$i" 2>/dev/null)
if [[ $out == *"aliased to"* ]]; then
out=${out%%\'*}
out=${out##*\`}
out=$(ali "$out")
elif [[ $out == *"$i is"* && $out != *"builtin"* && $out != *"keyword"* ]]; then
out=${out##*"$i is"}
else
out="$i"
fi
save="$save $out"
done <<< "$input"
echo "$save"
}
shopt -s expand_aliases
source ~/.bashrc
ali "$1"
示例输出
$ ./script "find . -type f | x grep 'linux' | wc -l"
/usr/bin/find . -type f | /usr/bin/xargs /bin/grep 'linux' | /usr/bin/wc -l
$ ./script "[[ -f test.txt ]] && ls"
[[ -f test.txt ]] && /bin/ls
$ ./script ":> test.txt"
:> test.txt
$ ./script "ll"
/bin/ls -lhtr
在某处出现间距/转义的一些错误,但应该很容易通过sed
进行修复,或只是echo -e $(./script "whatever")
应该在这里工作。
示例输出out =“”而不是out =“$ i”和hackish spacing fix
$ echo -e $(./script "find . -type f | x grep 'linux' | wc -l")
/usr/bin/find /usr/bin/xargs /bin/grep /usr/bin/wc
$ echo -e $(./script "[[ -f test.txt ]] && ls")
/bin/ls
$ echo -e $(./script ":> test.txt")
$ echo -e $(./script "ll")
/bin/ls
<强>更新强>
您想要的确切输出不应该太难以在脚本中更改。但更简单一点,将out="$i"
更改为out=""
并执行(或创建包装器)。还要注意,我在上面的脚本中添加了save=""
,因为有一个小问题,$ save被保存在某个地方并重复了第一个参数。
$ echo -e $(./script "find . -type f | x grep 'linux' | wc -l") | tr ' ' '\n'
/usr/bin/find
/usr/bin/xargs
/bin/grep
/usr/bin/wc
$ echo -e $(./test.sh "find . -type f | x grep 'linux' | wc -l; where ls") | tr ' ' '\n'
/usr/bin/find
/usr/bin/xargs
/bin/grep
/usr/bin/wc
/bin/ls
答案 1 :(得分:0)
您可以尝试首先在命令中识别可执行文件。你可以通过找出$ PATH变量中存在哪些令牌来做到这一点。 (它们必须在那里,否则您无法运行命令)请注意,当您在命令中有自己是路径中可执行文件的有效名称的参数时,此部分可能会变得棘手。
之后您可以为每个可执行文件运行'which'。 'which'本身就是一个unix工具。请参阅'man which'进行解释。
为了识别别名,您也可以简单地使用,因为“哪个[somealias]”将不会返回任何内容。
答案 2 :(得分:0)
bash回答
# split the pipeline.
# note this is insufficient: also need to split on ; && ||
# and be aware that the first char in the command might be ( or {
IFS='|' read -ra cmds << 'END'
find . -type f | x grep 'linux' | wc -l
END
for cmd in "${cmds[@]}"; do
set -- $cmd
case $(type -t $1) in
file) type $1 ;;
alias) [[ $(alias $1) =~ =\'([^\'[:blank:]]+) ]] &&
type ${BASH_REMATCH[1]}
;;
esac
done
find is /usr/bin/find
xargs is /usr/bin/xargs
wc is /usr/bin/wc