オペレーティングシステム:カーネル 2.6.x
シェル:POSIX互換シェル
便利:ビジーボックス1.25
質問:$VAR1から$VAR2の値を削除し、残りの値を$VAR3に出力する方法は?変数の各値はスペースで区切られます。
論理:
VAR1="1 2 3 4 5"
VAR2="1 3 5"
for i in $VAR1
if $i is not found in $VAR2; do
append $i to $VAR3
remove trailing space character
done
希望の出力:
VAR3="2 4"
ベストアンサー1
これは、スペースで区切られたスカラー変数に格納してエンコードする文字列のリストでなければなりません(文字列に対応するスペース文字が含まれていないと仮定)。
リスト/配列型変数とそれをサポートするシェルを使用する方が合理的です。たとえば、withzsh
と its${varX:|varY}
配列減算演算子:
VAR1=(1 2 3 4 5)
VAR2=(1 3 5)
VAR3=(${VAR1:|VAR2})
(VAR3=("${(@)VAR1:|VAR2}")
空の要素を保持)
sh
これで、配列をサポートしていないPOSIXに制限されている場合は、$@
より創造的でなければなりません。
リストの交差と減算のための標準コマンドはですcomm
。ただし、リストは改行で区切られたソートされたリストとして提供する必要があり、名前は引数として渡されるファイル内で指定する必要があります(-
標準入力を表すために使用できます)。
だからここでは使いにくいです。システムが/dev/fd/<n>
特殊ファイルをサポートしている場合:
VAR3=$(printf '%s\n' "$VAR1" | tr ' ' '\n' | sort | {
printf '%s\n' "$VAR2" | tr ' ' '\n' | sort |
comm -23 /dev/fd/3 -
} 3<&0 | paste -sd ' ' -)
または:
to_comm() { printf '%s\n' "$@" | tr ' ' '\n' | sort; }
from_comm() { paste -sd ' ' -; }
VAR3=$(to_comm "$VAR1" | { to_comm "$VAR2" | comm -23 /dev/fd/3 -;} 3<&0 |from_comm)
(また、$VAR1
少なくとも1つの要素があり(空のリストとは異なり、1つの空の要素を持つリストをどのように表現しますか)、要素に改行文字が含まれていないとします。
したがって、手動で実装することをお勧めします。最初のリストの各要素を繰り返して、2番目のリストで見つけます。
POSIX シェルでは、次のように Split+glob 演算子を使用できます。
IFS=' ' # split on space
set -o noglob # we don't want the glob part
VAR3= sep=
for i in $VAR1; do
case " $VAR2 " in
(*" $i "*) ;;
(*) VAR3=$VAR3$sep$i; sep=$IFS;;
esac
done
VAR1=' 2 3'
空の要素(inまたはなど)がある場合は使用できませんVAR1='1 3'
。これを行うには、|
分割規則が他のスペースではなく区切り文字(以下を参照)を使用することをお勧めします。
VAR1='*|foo bar||blah' VAR2='|blah'
IFS='|' # split on |
set -o noglob # we don't want the glob part
VAR3= sep=
for i in $VAR1''; do
# that $VAR1 split+glob invocation will split the content of $VAR1
# into "*", "foo bar", "", "blah" while with IFS=" ", the empty
# element wouldn't have been there as sequences of spaces would
# have been seen as a single separator.
case "|$VAR2|" in
(*"|$i|"*) ;;
(*) VAR3=$VAR3$sep$i; sep=$IFS;;
esac
done
inはPOSIXシェルでフィールドとして扱われず、合計に分割されるようにすること''
です(ほとんどPOSIX要件なので)。$VAR1''
foo|
"foo"
""
"foo"
$IFS
ターミネーター変える仕切り。
または、次のものを使用できますawk
。
export VAR1 VAR2
VAR3=$(awk 'BEGIN{
n = split(ENVIRON["VAR1"], a1, /[ ]/)
split(ENVIRON["VAR2"], a2, /[ ]/)
for (i in a2) in_a2[a2[i]]
for (i = 1; i <= n; i++)
if (! (a1[i] in in_a2)) $(++NF) = a1[i]
print}')