如何获取shell脚本中使用的命令列表?

时间:2016-12-21 10:22:26

标签: linux bash shell

我有一个超过1000行的shell脚本,我想检查脚本中使用的所有命令是否都安装在我的Linux操作系统中。 是否有任何工具可以获取shell脚本中使用的Linux命令列表? 或者我怎么能写一个可以为我做这个的小脚本?

该脚本在Ubuntu机器上成功运行,它作为C ++应用程序的一部分被调用。我们需要在运行具有有限功能的Linux的设备上运行相同的操作。我已手动识别,脚本运行的命令很少,而且设备操作系统上没有。在我们尝试安装这些命令之前,我想检查所有其他命令并立即安装所有命令。

提前致谢

4 个答案:

答案 0 :(得分:1)

我过去已经尝试过这个并得出结论,很难提供适用于所有脚本的解决方案。原因是每个具有复杂命令的脚本在使用shell功能时都有不同的方法。 如果是简单的线性脚本,它可能就像使用调试模式一样简单。 例如:bash -x script.sh 2>&1 | grep ^+ | awk '{print $2}' | sort -u 如果脚本有一些决定,那么你可以使用相同的方法,考虑到对于“else”情况,命令对于不同的参数仍然是相同的,或者是微不足道的(echo + exit)。

如果是一个复杂的脚本,我试图编写一个脚本,只在我自己做的同一个地方寻找命令。挑战是创建有助于识别所有使用的可能性的表达式,我想这对于大约80-90%的脚本是可行的,并且输出应该仅用作参考,因为它将包含无效数据(~20%)。

这是一个示例脚本,它将使用一种非常简单的方法解析自身(不同行上的单独命令,第一个单词将是命令):

# 1. Eliminate all quoted text
# 2. Eliminate all comments
# 3. Replace all delimiters between commands with new lines ( ; | && || )
# 4. extract the command from 1st column and print it once
cat $0 \
    | sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
    | sed -e "s/^[[:space:]]*#.*$//" -e "s/\([^\\]\)#[^\"']*$/\1/" \
    | sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
    | awk '{print $1}' | sort -u

输出是:

.
/
/g.
awk
cat
sed
sort
tr

还有更多需要考虑的案例(命令替换,别名等),1,2和3才刚刚开始,但它们仍将涵盖80%的大多数复杂脚本。 使用的正则表达式需要调整或扩展,以提高精度和特殊情况。

总之,如果您确实需要这样的东西,那么您可以编写如上所述的脚本,但在您自己验证之前不要相信输出。

答案 1 :(得分:0)

  1. export PATH=''添加到脚本的第二行。
  2. 执行your_script.sh 2>&1 > /dev/null | grep 'No such file or directory' | awk '{print $4;}' | grep -v '/' | sort | uniq | sed 's/.$//'

答案 2 :(得分:0)

如果您使用的是基于fedora / redhat的系统,则bash已使用--rpm-requires标志进行了修补

--rpm-requires:生成运行Shell脚本所需的文件列表。这意味着-n并受到与编译时错误检查检查相同的限制; 未解析命令替换,条件表达式和内置eval ,因此可能会丢失某些依赖项。

因此,当您运行以下命令时:

$ bash --rpm-requires script.sh
executable(command1)
function(function1)
function(function2)
executable(command2)
function(function3)

这里有一些限制:

    不会拾取
  1. 命令和进程替换以及条件表达式。因此,以下内容将被忽略:

    $(command)
    <(command)
    >(command)
    command1 && command2 || command3
    
  2. 不会拾取
  3. 命令作为字符串。因此,以下行将被忽略

    "/path/to/my/command"
    
  4. 包含外壳程序变量的
  5. 命令未列出。这通常是有道理的,因为 有些可能是某些脚本逻辑的结果,但即使以下内容也被忽略

    $HOME/bin/command
    

    不过,可以通过使用envsubst并将其运行为

    来绕过这一点
    $ bash --rpm-requires <(<script envsubst)
    

    但是,如果您使用shellcheck,则很可能引用了它,由于第2点,它仍将被忽略

因此,如果要使用检查脚本是否全部存在,可以执行以下操作:

while IFS='' read -r app; do
   [ "${app%%(*}" == "executable" ] || continue
   app="${app#*(}"; app="${app%)}";
   if [ "$(type -t "${app}")" != "builtin" ] &&                 \
       ! [ -x "$(command -v "${app}")" ]
   then
        echo "${app}: missing application"
   fi
done < <(bash --rpm-requires <(<"$0" envsubst) )

如果您的脚本包含的源文件可能包含各种功能和其他重要定义,则您可能想要执行类似的操作

bash --rpm-requires <(cat source1 source2 ... <(<script.sh envsubst))  

答案 3 :(得分:0)

基于@czvtools 的 answer,我添加了一些额外的检查来过滤掉错误的值:

#!/usr/bin/fish

if test "$argv[1]" = ""
    echo "Give path to command to be tested"
    exit 1
end

set commands (cat $argv \
    | sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
    | sed -e "s/^[[:space:]]*#.*\$//" -e "s/\([^\\]\)#[^\"']*\$/\1/" \
    | sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
    | awk '{print $1}' | sort -u)

for command in $commands
    if command -q -- $command
        set -a resolved (realpath (which $command))
    end
end

set resolved (string join0 $resolved | sort -z -u | string split0)

for command in $resolved
    echo $command
end