5

私は知っています、これは何十億回も尋ねられましたが、私はまだ私の特定のケースに最適な解決策を見つけていません.

次のような文字列を受け取ります。

VAR1="some text here" VAR2='some another text' some script --with --some=args

このように文字列を分割するにはどうすればよいですか: (純粋なbashで最も望ましい)

VAR1="some text here"
VAR2='some another text'
some script --with --some=args

set -- $str結果VAR1="some

set -- "$str"文字列全体を返します

eval set -- "$str"結果VAR1=some text here

確かに、によって返される文字列に引用符を追加できますが、eval非常に信頼できない入力が得られるためeval、まったくオプションではありません。

重要: 0 から無制限の VAR を指定でき、一重引用符または二重引用符で囲むことができます

また、VARここでは偽の名前であり、実際には何でもかまいません。

ありがとう。

4

4 に答える 4

3

うーん、私はパーティーに遅れているようです :)

スクリプトの前に渡された環境変数を処理する方法は次のとおりです。

まず第一に、escape_args関数は渡された変数の「内側」のスペースをエスケープします。

なのでユーザーパスならこうVAR="foo bar"なりVAR=foo\0040barます。

function escape_args {
  local str=''
  local opt=''
  for c in $1; do
    if [[ "$c" =~ ^[[:alnum:]]+=[\"|\'] ]]; then
      if [[ "${c: -1}" =~ [\"|\']  ]]; then
        str="$str $( echo $c | xargs )"
      else
        # first opt chunk
        # entering collector
        opt="$c"
      fi
    else
      if [ -z "$opt" ]; then
        # not inside collector
        str="$str $c"
      else
        # inside collector
        if [[ "${c: -1}" =~ [\"|\']  ]]; then
          # last opt chunk
          # adding collected chunks and this last one to str
          str="$str $( echo "$opt\0040$c" | xargs )"
          # leaving collector
          opt=''
        else
          # middle opt chunk
          opt="$opt\0040$c"
        fi
      fi
    fi
  done
  echo "$str"
}

入力の変更されたバージョンに対してテストしてみましょう。

s="VAR1=\"some text here\" VAR2='some another text' VAR3=\"noSpaces\" VAR4='noSpacesToo' VAR5=noSpacesNoQuotes some script --with --some=args"

echo $(escape_args "$s")

VAR1=some\0040text\0040here VAR2=some\0040another\0040text VAR3=noSpaces VAR4=noSpacesToo VAR5=noSpacesNoQuotes some script --with --some=args

すべての変数はスペースでエスケープされ、引用符が削除されているため、declare正しく機能します。

これで、入力の一部を反復処理できます。

vars を宣言してスクリプトを実行する方法の例を次に示します。

cmd=''
for c in $(escape_args "$s"); do
  [[ "$c" =~ ^[[:alnum:]]+= ]] && declare "$(echo -e $c)" && continue
  cmd="$cmd $c"
done

echo VAR1 is set to $VAR1
echo VAR2 is set to $VAR2
echo VAR3 is set to $VAR3
echo VAR4 is set to $VAR4
echo VAR5 is set to $VAR5
echo $cmd

この反復子は、次の 2 つの単純なことを行っています。

  • SOME_VAR=式に一致するチャンクの場合は var を宣言する
  • それ以外の場合は、最終的なコマンドにチャンクを追加します

したがって、出力は次のようになります。

VAR1 is set to some text here
VAR2 is set to some another text
VAR3 is set to noSpaces
VAR4 is set to noSpacesToo
VAR5 is set to noSpacesNoQuotes
some script --with --some=args

これはあなたのニーズに近いですか?

于 2012-10-10T20:26:22.447 に答える
3

純粋な bash とはかけ離れていますが、Python には、shlexシェル互換の字句解析を提供しようとするモジュールがあります。

>>> import shlex, pprint
>>> pprint.pprint(shlex.split('''VAR1="some text here" VAR2='some another text' some script --with --some=args'''))
['VAR1=some text here',
 'VAR2=some another text',
 'some',
 'script',
 '--with',
 '--some=args']

次のより完全な例では、bash からこの Python モジュールを使用し、明確なトランスポートを提供する NUL 区切りのストリームを使用します。

shlex() {
  python -c $'import sys, shlex\nfor arg in shlex.split(sys.stdin):\n\tsys.stdout.write(arg)\n\tsys.stdout.write(\"\\0\")'
}
args=()
while IFS='' read -r -d ''; do
  args+=( "$REPLY" )
done < <(shlex <<<$'VAR1="some text here" VAR2=\'some another text\' some script --with --some=args')
printf '%s\n' "${args[@]}"
于 2012-10-10T14:14:38.890 に答える
2

次の純粋な bash コードで遊ぶことができます。入力を1文字ずつ調べて、引用符の内側/外側に関するフラグを保持しようとします。

#! /bin/bash 
string=$(cat <<'EOF'
VAR1="some text here" VAR2='some another text' VAR3="a'b" VAR4='a"b' VAR5="a\"b" VAR6='a'"'"'b' some script --with --some=args
EOF
)
echo "$string"

results=()
result=''
inside=''
for (( i=0 ; i<${#string} ; i++ )) ; do
    char=${string:i:1}
    if [[ $inside ]] ; then
        if [[ $char == \\ ]] ; then
            if [[ $inside=='"' && ${string:i+1:1} == '"' ]] ; then
                let i++
                char=$inside
            fi
        elif [[ $char == $inside ]] ; then
            inside=''
        fi
    else
        if [[ $char == ["'"'"'] ]] ; then
            inside=$char
        elif [[ $char == ' ' ]] ; then
            char=''
            results+=("$result")
            result=''
        fi
    fi
    result+=$char
done
if [[ $inside ]] ; then
    echo Error parsing "$result"
    exit 1
fi

for r in "${results[@]}" ; do
    echo "< $r >"
done
于 2012-10-10T15:28:32.950 に答える
0

ストリーム エディターを使用してテキストを変更できます。最初に正規表現を使用して変数を取得し、それらを空の引用符に置き換えることができます。先頭と末尾に引用符を追加します。この段階では、次のものが必要です。

VAR1="some text here" 
VAR2='some another text'

元の文字列は次のようになります。

"""""some script --with --some=args"

標準のコマンド ライン解析では、次が返されます。

""
""
"some script --with --some=args"

空の文字列を捨てると、必要なものが残るはずです。これはハックな (潜在的な) 解決策であり、このようなものを使用する前に、少しテスト/検討することをお勧めします。

于 2012-10-10T16:55:04.410 に答える