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

时间:2015-07-11 12:48:17

标签: bash shell awk scripting split

我的最终目标是以干净的方式遍历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简单,因为它处理自动引用的参数,转义空格,全局扩展等...所以我不必使用复杂的awksed命令自行处理。

单个命令可以正常工作,如下所示:

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

但显然(因为数组定义中的&#39 ;;'),当我在一行中使用多个命令时,它会失败:

$ 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 '&&'

为了使其工作,我将不得不在遇到;&&||时将命令字符串拆分为多个部分,而不会忘记这些令牌被转义的情况或引用,然后告别简单 ...我甚至不知道我是否能够以一种好的方式解析这个问题,并且掌握了我目前的知识{ {1}}和sed ...

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

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

2 个答案:

答案 0 :(得分:1)

现在我只能生产这样的东西:

#!/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

答案 1 :(得分:0)

你可以做得更好,但并非所有情况都可以:

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