Bash配列でのパラメーター置換の使用


8

Bash配列に読み込む必要があるfile.txtがあります。次に、スペース、二重引用符、およびすべてのエントリの最初のカンマ除くすべてを削除する必要があります。これが私がどこまで得たかです:

$ cat file.txt
10,this
2 0 , i s
30,"all"
40,I
50,n,e,e,d,2
60",s e,e"

$ cat script.sh
#!/bin/bash
readarray -t ARRAY<$1
ARRAY=( "${ARRAY[@]// /}" )
ARRAY=( "${ARRAY[@]//\"/}" )
for ELEMENT in "${ARRAY[@]}";do
    echo "|ELEMENT|$ELEMENT|"
done

$ ./script.sh file.txt
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,n,e,e,d,2|
|ELEMENT|60,se,e|

コンマの状況を除いて、それは素晴らしい働きをします。この猫のスキンを作成する方法は複数あることは承知していますが、これは一部のスクリプトが大きいため、ここに到達するにはパラメーター置換を使用したいと思います。

|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|

これはパラメーターの置換によって可能ですか?


3
テキストを配列に保持する必要がある理由はありますか。また、たとえば、データを処理しawkたりsed処理したりできないのはなぜですか。
クサラナンダ

@Jeff-配列のループは、私が取り組んでいるより大きなスクリプトに実装するのは悪夢になります。
Jon Red

3
@JonRed私はあなたが何をしているのかわからないので、あなたが問題を選択できない可能性は十分にありますが、一般的に、シェルでそのような複雑な文字列の曲芸をしているのを見つけたとき、それはあなたが非常に良いことを示しています実際のプログラミング言語を使用する必要があります。シェルはプログラミング言語として設計されておらず、1つのシェルとして使用することはできますが、より複雑なものにはあまり適していません。perlやpythonなどのスクリプト言語への切り替えを検討することを強くお勧めします。
terdon

@terdonおかしい、この投稿を読む前に、同僚にほぼ同じことを言っただけだ。基本的にこれはこのスクリプトの最終バージョンであり、それ以上の要件がある場合はPerlでの書き換えが必要になると述べました。ええ、私は間違いなく同意します
ジョンレッド

回答:


9

配列sed ロードするに削除する必要があるものを削除します(小文字の変数名にも注意してください。一般に、シェルスクリプトでは大文字の変数を使用しないことをお勧めします)。

#!/bin/bash
readarray -t array< <(sed 's/"//g; s/  *//g; s/,/"/; s/,//g; s/"/,/' "$1")
for element in "${array[@]}";do
    echo "|ELEMENT|$element|"
done

これにより、サンプルファイルに次の出力が生成されます。

$ foo.sh file 
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|

本当にパラメータ置換を使用する必要がある場合は、次のようなことを試してください:

#!/bin/bash
readarray -t array< "$1"
array=( "${array[@]// /}" )
array=( "${array[@]//\"/}" )
array=( "${array[@]/,/\"}" )
array=( "${array[@]//,/}" )
array=( "${array[@]/\"/,}" )

for element in "${array[@]}"; do
    echo "|ELEMENT|$element|"
done

1
@JonRedパラメータを置換したバージョンを追加しましたが、複雑で面倒で見苦しいです。シェルでこのようなことをすることは、めったに良い考えではありません。
terdon

1
スペースと二重引用符の両方を削除した場合、これらの文字はの代わりに使用できるようになりますRANDOMTEXTTHATWILLNEVERBEINTHEFILE
クサラナンダ

1
@Kusalanandaええ、私はあなたの答えを読んだだけです。それを考えるべきだった!ありがとう:)
terdon

質問に直接回答し、私の推奨ソリューションが理想的でない理由を示し、最も実行可能な代替案を提供します。あなたが勝ち、ベストアンサー。
Jon Red

10

私が見る限り、bashその出力を作成するために配列に読み込む必要はありません。

$ sed 's/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/' <file
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|

このsed式は、スペースと二重引用符を削除し、最初のコンマをスペースに置き換えます(この時点では、文字列に他のスペースはありません)。他のすべてのコンマを削除し、最初のコンマを復元し、追加のデータを付加して追加します。

または、GNUを使用しますsed

sed 's/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/' <file

(標準でsedは、コマンドのフラグとして、2およびコマンドgへのフラグとしてはサポートされていませんs)。


1
GNU sedを使用's/,//2gすると、2番目から開始して
glenn jackman

2
そして、最後の2つのs ///コマンドを使用することもできますs/.*/|ELEMENT|&|/が、sedの方が努力が必要な場合があります。
グレン・ジャックマン

1
@glennjackmanたぶん、それはかなりきちんと見えます。
クサラナンダ

ええ、これは大きなスクリプトの一部です。出力だけでなく、配列も必要です。したがって、パラメーター置換に私の興味があります。これで配列をループできますが、実装するのは悪夢になります。Terndonは、sedを使用してループのないソリューションを提供しました。これは、パラメーターの置換がノーゴーである場合に頼りになるでしょう。
Jon Red

ただし、配列の使用に縛られていなければ、これが最善の解決策です。
Jon Red

9
ELEMENT='50,n,e,e,d,2'
IFS=, read -r first rest <<<"$ELEMENT"
printf "%s,%s\n" "$first" "${rest//,/}"
50,need2

ALLCAPS変数名を使用する習慣から抜け出します。最終的には、PATHなどの重要な「システム」変数と衝突し、コードを破壊します。


パラメータ置換ではありません。しかし、ALLCAPS変数名がBashの悪い習慣であることを知りませんでした。あなたは良い点を作ります、大雑把なグーグルは間違いなく確認します。私のスタイルを改善してくれてありがとう!:)
ジョンレッド

1
その人が書いた場所の質問に答えてPATH=something; ls $PATHから、そのls: command not foundエラーについて疑問に思いました。
グレン・ジャックマン

1
約100内蔵のすべて大文字で命名された変数(クリック・スルーがあります。このmanページのリンク)を参照してくださいするには...
ジェフ・シャラー

8

[これは本質的にはグレン・ジャックマンの答えのより完全に開発されたバージョンです ]

最初のコンマを区切り文字として使用して、除去されたキーと値から連想配列を作成します。

declare -A arr
while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt
for k in "${!arr[@]}"; do 
  printf '|ELEMENT|%s,%s|\n' "$k" "${arr[$k]}"
done
|ELEMENT|20,is|
|ELEMENT|10,this|
|ELEMENT|50,need2|
|ELEMENT|40,I|
|ELEMENT|60,see|
|ELEMENT|30,all|

6

配列をループして中間変数を使用できます。

for((i=0; i < "${#ARRAY[@]}"; i++))
do
  rest="${ARRAY[i]#*,}"
  ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}"
done

これrestは、最初のコンマの後の部分に割り当てられます。次に、3つの部分を連結して元の変数に戻します。

  • 最初のカンマの前の部分
  • コンマ
  • restすべてのコンマを何も置き換えない

これは私の最初の考えであり、例としては十分単純ですが、これは、配列が大規模で、ループがすでに存在する、全体のスクリプトの一部です。これは間違いなく機能しますが、私が取り組んでいるより大きなプロジェクトに実装するのは非常に面倒です。
Jon Red

1
けっこうだ; 私は制限内で答えようとしました(パラメーター拡張のみ)。
ジェフシャラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.