2015-07-11 2 views
1

Моя конечная цель состоит в том, чтобы выполнить итерацию по всем параметрам, используемым в последней командной строке в bash, чтобы найти любой путь к каталогу. Пример того, что я хотел бы:Параметры командной строки 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 таким образом, потому что он обрабатывает автоматически цитируемые параметры, спасся пространство, расширение Глобо и т.д ... Так что я не должен сам справиться со сложной командой awk или sed.

Он отлично работает с отдельными командами, например:

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

Итак, чтобы сделать его работу, я бы, чтобы разбить командную строку на части, когда ;, && или || встречается, не забывая при этом случай, когда эти маркеры экранируются или цитировал, а затем прощались с простота ... Я даже не знаю, если я могу еще разобрать это в хорошем смысле, с нынешних знаний у меня есть о sed и awk ...

Что бы самым чистым (и самое простое) решение, чтобы получить все параметры команды в массив, обрабатывая возможность цитируемых параметров, экранированных символов, и нескольких команд в одной строке?

Возможно, это совсем не дубликат, но я нигде не нашел никакого реального решения.

+0

Итак, каково ожидаемое содержимое вашего массива 'args' в таких командах:' echo '1 2 3' && echo "AB" C' –

+0

@Eugeniu Rosca: что-то вроде '(echo '1 2 3' '&& 'echo "AB" C) 'или' (echo' 1 2 3 'echo "AB" C) '.Важно то, что каждый эффективный параметр присутствует, так что я могу проверить их с помощью некоторой вещи, например '[[-d $ {args [$ i]}]]' в цикле – yolenoyer

+0

Вместо 'eval 'args = ($ last_com) ", попробуйте' IFS = '' args = ($ last_com) ' –

ответ

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 

Version 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 
+0

Это не идеальное решение, но поставит его как ответ через некоторое время, если нет лучшего ответа ... спасибо вам, ваша помощь – yolenoyer

+0

Возможно, у кого-то есть действительно простой способ сделать это , Не помещайте вопрос, как уже было сказано. –

+0

Поскольку вы действительно хотите извлечь некоторые пути каталога/файла из истории команд, я думаю, вы можете просто удалить ВСЕ специальные символы из '$ last_cmd', такие как:' [] | *^# + & $!% ' , Проверьте обновленную версию. –

0

Вы можете сделать немного лучше, хотя и не во всех случаях, с:

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

Спасибо, это заставило меня изучить некоторые вещи с помощью 'bash', но он не очень удачно разбивает слова, когда в кавычках есть пробелы; но я должен посмотреть на это немного больше. – yolenoyer

Смежные вопросы