13

では、スペースで区切られた要素を含む可能性arrayのある PATHのような環境変数を に変換し、スペースを含む要素が単語分割を引き起こさず、「複数の要素」として表示されるようにしたいと考えています。


PATH_VARIABLE問題の変数としましょう。

を変数un:dodecaedro:per:tirare:per:i danniの内容とします。

ではなく、目的の_to要素を持つこと目的と しています。 array67

0) un
1) dodecaedro
2) per
3) tirare
4) per
5) i danni

「トリッキーな」エントリは、スペースで区切られた値の場合があります: i danni.

これを達成するための最もエレガント 正しい方法を探しています。

バージョンで動作する必要があります:v3.2.48(1)-release


では、これは次のように美しく行われます:

>>> v='un:dodecaedro:per:tirare:per:i danni'
>>> len(v.split(':'))
6

動作します。私が探しているものを示しています。


でこれを行う最良の方法は何ですか?

私の試みを具体的に改善できます4か?

ここで私の試み


#!/bin/bash

PATH_VARIABLE='un:dodecaedro:per:tirare:per:i danni'

# WRONG
a1=($(echo $PATH_VARIABLE | tr ':' '\n'))

# WRONG
a2=($(
  while read path_component; do
  echo "$path_component"
  done < <(echo "$PATH_VARIABLE" | tr ':' '\n')
))

# WORKS, it is elegant.. but I have no bash 4!
# readarray -t a3 < <(echo "$PATH_VARIABLE" | tr ':' '\n')

# WORKS, but it looks "clunky" to me :(
i=0
while read line; do
  a4[i++]=$line
done < <(echo "$PATH_VARIABLE" | tr ':' '\n')

n=${#a4[@]}
for ((i=0; i < n; i++)); do
  printf '%2d) %s\n' "$i" "${a4[i]}"
done

私の環境

v3.2.48(1)-リリース

OS X v10.8.3 (ビルド 12D78)


4

3 に答える 3

7
f() {
  local IFS=:
  local foo
  set -f # Disable glob expansion
  foo=( $@ ) # Deliberately unquoted 
  set +f
  printf '%d\n' "${#foo[@]}"
  printf '%s\n' "${foo[@]}"
}

f 'un:dodecaedro:per:tirare:per:i danni'
6
un
dodecaedro
per
tirare
per
i danni

Jim McNamara の回答を変更すると、IFS をリセットできます。

oIFS="$IFS"
foo='un:dodecaedro:per:tirare:per:i danni'
IFS=: arr=( $foo )
IFS="$oIFS"

私は関数スコープを好みます。なぜなら、IFS の変更がグローバル スコープに流れ込むのを防ぐためです。

編集と説明:

明確にするために、2 番目の例では、IFS 設定によってグローバル変数が変更されます。これの間の顕著な違い:

IFS=: arr=( $foo )

この:

IFS=: read -a arr <<< "$foo"

前者は 2 つの変数の割り当てでコマンドはなく、後者は単純なコマンドです ( の単純なコマンドを参照してくださいman (1) bash)。

デモンストレーション:

$ echo "$BASH_VERSION"
3.2.48(1)-release
$ echo "$IFS"


$ foo='un:dodecaedro:per:tirare:per:i danni'
$ IFS=: read -a arr <<< "$foo"
$ echo "${#arr[@]}"
6
$ echo "$IFS"


$ IFS=: arr1=( $foo )
$ echo "${#arr1[@]}"
6
$ echo "$IFS"
:
于 2013-04-03T03:19:49.753 に答える
7
# Right. Add -d '' if PATH members may contain newlines.
IFS=: read -ra myPath <<<"$PATH"

# Wrong!
IFS=: myPath=($PATH)

# Wrong!
IFS=:
for x in $PATH; do ...

# How to do it wrong right...
# Works around some but not all word split problems
# For portability, some extra wrappers are needed and it's even harder.
function stupidSplit {
    if [[ -z $3 ]]; then
        return 1
    elif [[ $- != *f* ]]; then
        trap 'trap RETURN; set +f' RETURN
        set -f
    fi
    IFS=$3 command eval "${1}=(\$${2})"
}

function main {
    typeset -a myPath
    if ! stupidSplit myPath PATH :; then
        echo "Don't pass stupid stuff to stupidSplit" >&2
        return 1
    fi
}

main

ルール #1: 代替手段がない場合を除き、複合データ構造を文字列またはストリームに詰め込まないでください。PATHあなたがそれに対処しなければならない1つのケースです。

ルール 2: 単語とフィールドの分割は絶対に避けてください。Bash などの非ミニマリスト シェルでは、パラメーターの値に単語分割を適用する正当な理由はほとんどありません。ほとんどすべての初心者の落とし穴は、IFS で単語分割を行わないことで回避できます。常に引用します。

于 2013-04-03T21:20:28.403 に答える
6

検討:

$ foo='1:2 3:4 5:6'
$ IFS=':'; arr=($foo)
$ echo "${arr[0]}"
1
$ echo "${arr[1]}"
2 3
$ echo "${arr[2]}"
4 5
$ echo "${arr[3]}"
6

ああ、答えをフォーマットするのに時間がかかりすぎました... +1 @kojiro。

于 2013-04-03T03:29:16.940 に答える