Bash: разделить строку точно так же, как Readline разделит ее - PullRequest
3 голосов
/ 20 января 2012

Учитывая строковое представление произвольного Bash " простая команда ", как я могу разбить его на массив, содержащий его отдельные "части", то есть имя команды и отдельные параметры, , как сама оболочка (т. е. Readline) будет разбивать ее при ее анализе и принятии решения, какой исполняемый файл / функцию запустить и какие параметры передать?


Мой конкретный вариант использования требует анализа пользовательских определений псевдонимов. Например. псевдоним может быть определен как:

alias c2="cut -d' ' -f2"  # just an example... arbitrary commands should be handled!

И вот как мой bash-скрипт попытается его проанализировать:

alias_name="c2"
alias_definition=$(alias -p | grep "^alias $alias_name=") # "alias c2='cut -d'\'' '\'' -f2'"
alias_command=${alias_definition##alias $alias_name=}     # "'cut -d'\'' '\'' -f2'"
alias_command=$(eval "echo $alias_command")               # "cut -d' ' -f2"

alias_parts=($alias_command) # WRONG - SPLITS AT EVERY WHITESPACE!

echo "command name: ${alias_parts[0]}"

for (( i=1; i <= ${#alias_parts}; i++ )); do
  echo "parameter $i : ${alias_parts[$i]}"
done

Выход:

command name: cut
parameter 1 : -d'
parameter 2 : '
parameter 3 : -f2

Желаемый выход:

command name: cut
argument 1  : -d' '
argument 2  : -f2


Чем мне нужно заменить строку alias_parts=($alias_command), чтобы добиться этого?

Ответы [ 5 ]

2 голосов
/ 21 января 2012

как сказал l0b0, это не readline.Это сама оболочка делает раскол.Поэтому используйте синтаксический анализ самой оболочки.

alias c2="cut -d' ' -f2"

split_parts() {
    alias_parts=("$@")
}

alias_defn=$(alias c2)
# 2 evals needed to get rid of quotes
eval eval split_parts ${alias_defn#alias c2=}

for (( i=0; i < ${#alias_parts}; i++ )); do
  echo "parameter $i : \"${alias_parts[$i]}\""
done

выводит

parameter 0 : "cut"
parameter 1 : "-d "
parameter 2 : "-f2"

Обратите внимание, что -d включает в себя конечный пробел, который фактически видит оболочка.

1 голос
/ 24 января 2012

Чтобы свести к минимуму решение «злого отто»:

alias c2="cut -d' ' -f2"
alias_definition=$(alias c2)
eval eval alias_parts=( "${alias_definition#alias c2=}" )

Вы можете использовать команду `объявлять -p 'для быстрой печати массива:

$ declare -p alias_parts
declare -a alias_parts='([0]="cut" [1]="-d " [2]="-f2")'

Также полезным может быть` printf% q ', чтобы процитировать аргумент "так, чтобы его можно было использовать в качестве ввода оболочки" (из: help printf):

$ printf %q ${alias_parts[1]}
-d\

Фредди Вулто
http://fvue.nl/wiki/Bash

0 голосов
/ 20 января 2012

Встроенный set может использоваться для разделения строк.

bash$ set -- cut -d ' ' -f2

bash$ echo "'$3'"
' '

Редактировать : Если строка, которую вы хотите разделить, уже находится в переменной, это оченьхитрее.Вы можете поиграть с eval, но в этом случае я бы сказал, что это усложняет, а не упрощает их.

bash$ a="cut -d ' ' -f2"

bash$ eval set -- $a  # No quoting!

bash$ echo "'$3'"
' '
0 голосов
/ 21 января 2012

Если мы поместим каждый из аргументов alias_command в отдельную строку, а затем (локально) установим IFS=\n, мы закончим:

parsealias ()
{
   alias_command_spaces=$(eval "echo $(alias $1)" | sed -e "s/alias $1=//") # "cut -d' ' -f2"
   alias_command_nl=$(eval each_arg_on_new_line $alias_command_spaces)      # "cut\n-d' '\n-f2"
   local IFS=$'\n' # split on newlines, not on spaces
   alias_parts=($alias_command_nl) # each line becomes an array element, just what we need
   # now do useful things with alias_parts ....
}

Теперь нам нужно только написать команду each_arg_on_new_lineиспользуется выше, например:

#!/usr/bin/env perl

foreach (@ARGV) {
  s/(\s+)/'$1'/g; # put spaces whithin quotes
  print "$_\n";
}
0 голосов
/ 20 января 2012

Это не readline разбиение, это getopt или getopts. Например :

params="$(getopt -o d:h -l directory:,help --name "$0" -- "$@")"

eval set -- "$params"
unset params

while true
do
    case "${1-}" in
        -d|--directory)
            directory="$2"
            shift 2
            ;;
        -h|--help)
            usage
            exit
            ;;
        --)
            shift
            if [ "${1+defined}" = defined ]
            then
                usage
            fi
            break
            ;;
        *)
            usage
            ;;
    esac
done
...