如何在文本中查找使用过的命令和别名?

时间:2014-01-16 23:42:45

标签: regex bash shell zsh

假设您的历史记录文件中有类似于以下的shell历史记录:

find . -type f | x grep 'linux' | wc -l

注意:xxargs命令的别名。

我想写一个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

请问如何编写此功能?

3 个答案:

答案 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