以智能和干净的方式拆分 bash 命令参数

Split bash command parameters in a smart and clean way

我的最终目标是以干净的方式遍历 bash 中最后一个命令行中使用的所有参数,以便找到目录的任何路径。 我希望的示例:

$ cp some_file.txt /some/existing/folder; touch a_new_file.txt
$ my_find_func
Found "/some/existing/folder" in the last command.

我的问题是关于以正确的方式拆分最后一个命令,以便处理所有可能的情况。现在我正在使用这样的东西:

function myfunc()
{
    last_com="$(history|tail -n2|head -n1|sed -n 's/^\s*[0-9]*\s*//p')"
    eval "args=( $last_com )"
    # Note: I don't care about the security issue that eval can cause

    for arg in "${args[@]}"; do
        echo "$arg"
    done
}

我喜欢以这种方式使用 eval 简单性 ,因为它可以处理自动引用的参数、转义空格、glob 扩展等...所以我不不必自己用复杂的 awksed 命令来处理这个问题。

它适用于单个命令,如下所示:

/afac/soq $ cd ..
/afac $ myfunc 
cd
..
/afac $ touch "some file.txt"
/afac $ myfunc 
touch
some file.txt

但很明显(因为数组定义中的';'),当我在一行中使用多个命令时它失败了:

$ touch a_file; rm a_file
$ myfunc
bash: syntax error near unexpected token ';'
$ touch a_file && rm a_file
$ myfunc
bash: syntax error near unexpected token '&&'

所以为了让它工作,我必须在遇到 ;&&|| 时将命令字符串拆分成多个部分,同时不要忘记这些标记时的情况被转义或引用,然后告别 simplicity...我什至不知道我是否能够以一种好的方式解析它,以我目前的知识sedawk...

将命令的所有参数放入数组的最干净(也是最简单)的解决方案是什么,处理引用参数、转义字符、 多个参数的可能性单行命令?

它可能完全重复,但我没有在任何地方找到任何真正的解决方案。

虽然不是所有情况,但您可以做得更好:

function myfunc(){
 set -- $(history 2 | sed 's/[ 0-9]*//;1q')
 for arg
 do echo "$arg"
 done
}

现在我只能制作这样的东西:

#!/bin/bash

last_cmd='echo 1 2 "3 4" &  && echo "A B" C || echo D "E F" &  '

# Convert any of: &, && or || to semicolon
cmd=$(sed -e 's/&\?[ ]*&&/;/g' -e 's/&\?[ ]*||/;/g' -e 's/&[ ]*$//' <<< "$last_cmd")
# TODO: get rid of/convert any other symbols creating issues to eval
echo "processed cmd: $cmd"

# split the command string using semicolon as delimiter
IFS=';'
cmd_arr=($cmd)
IFS=' '
args=()
for onecmd in "${cmd_arr[@]}"; do
    eval "args+=($onecmd)"
done

for arg in "${args[@]}"; do
    echo "$arg"
done

版本 2

last_cmd='echo 1 2 "3 4"  && echo "A B" C || echo D ["E F"] $!%#^* '

# Remove ALL special characters not having a chance to appear in a pathname
cmd=$(sed 's/[][|*^#+&$!%]//g' <<< "$last_cmd")
echo "processed cmd: $cmd"

IFS=' ' eval "args=($cmd)"
for arg in "${args[@]}"; do
    echo "$arg"
done