Bash 3.2.52では、すべての非環境変数(すべて自己宣言された変数)をecho
したいのですが。
すべての変数を出力するこのコマンドを実行すると、理解できない出力が得られました。これは、すでに使用しているset -x && clear -r
モードと競合しているようです。
diff -U 1 <(set -o posix ; set |cut -d= -f1) <(
exec bash -ic 'set -o posix ; set' | cut -d= -f1) |
grep '^[-][^-]' |
cut -d- -f2 |
grep -vE '^(COLUMNS|HISTFILESIZE|HISTSIZE|LINES|PIPESTATUS)$'
このようなリストを作成するには、echo
またはprintf
(またはその他の「より単純な」)操作が必要です。
このバージョンのBashで可能な場合、どのようにしてこれを達成できますか?
GNU Parallelにはenv_parallel
が含まれています。 env_parallel
の一部には、サポートされているシェルのすべての変数がリストされています。
bash
の場合、このコードは次のとおりです。
_names_of_VARIABLES() {
compgen -A variable
}
_bodies_of_VARIABLES() {
typeset -p "$@"
}
したがって、すべての変数を指定して、ユーザーが設定した変数を特定する必要があります。我々はcanbash
の特定のバージョンによって設定されたすべての変数をハードコーディングすることでそれを行いますが、bash
は将来さらに多くの変数を設定する可能性があるため、これはあまり将来の証明にはなりませんバージョン($BASH_MYVAR
は、ユーザーまたはbash
の将来のバージョンによって設定されますか?).
したがって、代わりにenv_parallel
は、クリーンな環境を定義してenv_parallel --session
を実行するように求めます。これは、前に定義された名前をリストすることによって$PARALLEL_IGNORED_NAMES
を設定します(上記のコードを使用)。
ユーザーが変数を設定した環境で後で実行すると、クリーンな環境と現在の環境の違いを簡単に理解できます。
env_parallel --session
を使用すると、セッションごとにクリーンな環境を定義できますが、セッション間で使用できる参照環境が必要な場合は、変数のリストをファイルに保存するだけです。そう:
# In clean envronment
compgen -A variable > ~/.clean-env-vars
# In environment with user defined vars
compgen -A variable | grep -Fxvf ~/.clean-env-vars
例えば:
#!/bin/bash
# In clean envronment
compgen -A variable > ~/.clean-env-vars
myvar=4
yourvar=3
# In environment with user defined vars
compgen -A variable | grep -Fxvf ~/.clean-env-vars
# This prints:
# myvar
# yourvar
# PIPESTATUS (which is set when running a pipe)
これを~/.bashrc
に追加してください
pre () {
set -o posix;set > /tmp/tmp-original-envs
}
post () {
set -o posix;set > /tmp/tmp-new-envs
diff /tmp/tmp-original-envs /tmp/tmp-new-envs | grep -v BASH_LINENO | grep -v FUNCNAME | grep '>'
}
次に、変更の前にpre
を実行し、後でpost
を実行します
例:
[localhost]$ pre
[localhost]$ test=new
[localhost]$ post
> test=new
[localhost]$
この解決策は、すべての非環境変数をリストする道へとあなたを導いてくれると思います。 ITEM=$'VALUE\nSTRANGE=QUARK'
やALSO=$'VALUE\ndeclare -r LANG=fake'
のように定義された変数、つまり改行が埋め込まれた変数を確実に処理します。
for item in $(declare -p | awk '$2 !~ /[aAx]/ && $3 ~ /.=/ {print substr($3,1,match($3,"=")-1)}')
do
if [[ -n "$item" ]] && ! { env -0 | grep -q $'\0'"${item}="; }
then
printf ">> %s = %s <<\n" "$item" "${!item}"
# printf "%s\n" "$item"
fi
done
printf
ステートメントを調整して、変数名だけを出力したり、配列に追加したり、その他の必要なことを行うことができます。
私のbashバージョンは3.2.57です
私はset +o posix
を使用します。これにより、各変数の定数が1行で表示されるように動作が変更されます
フィルタリングするにはsort| uniq -c
を使用し、値を1 lに維持します
bash-3.2$ bar=" foobar= "
bash-3.2$ foo="0
> 1
> 2 "
今私は2つの変数がありますfoobar
bash-3.2$ A=$( ( ( set +o posix ; set |cut -d= -f1 ) ;
( bash -ic 'set +o posix ; set |cut -d= -f1' )
) | sort | uniq -c | sed 's/^ *1 //p;d' |
grep -vE '^(COLUMNS|HISTFILESIZE|HISTSIZE|LINES|PIPESTATUS|BASH_EXECUTION_STRING)$' )
bash-3.2$ echo $A
bar foo
Kludgy、bashのみのソリューション:
(setn(){
set | sed '/^{/,/^}/d;/=/!d;/^BASH[A-Z_]*=\|^PPID=\|^SHLVL=\|^PIPESTATUS=\|^HISTSIZE=\|^HISTFILESIZE=\|^_[a-z_]*=/d' | sort
}
export -f setn
comm -23 <(setn) <("$BASH" -ic setn))
sed
を介して他の変数を除外する必要がある場合もあります(またはBASH..
パターンをより厳密にする)。 /_[a-z_]*=/
は、_backup_glob
や_xspecs
などのプログラム可能な補完によって定義された変数を除外することになっています。それももっと厳しくすることができます。
bash
は、変な文字を含む変数の値を$'...'
形式で出力するため、次のように処理する必要があります。
foo='
bar
baz
'
...
foo=$'\nbar\nbaz\n'
bash
は、namesに改行が含まれているenvvarsからシェル変数を作成しないため、このハッキングをこのようなトリックで破る方法はありません。
compgen -A variable
を使用するソリューション で問題が発生したようです。質問の本文で言及したset -x
のせいかもしれませんが、わかりません。
今はcompgen
なし。 このコメント から始めましょう。それは言う:
grep -vxF -f <( declare -x -p ) <( declare -p )
部分的に改善します
grep -vxF -f <( declare -x -p | grep -o '[^ =]*=') <( declare -p | grep -o '[^ =]*=')
現在、いくつかの問題があります。
コンテンツに=
を含む変数は、上記のコードで誤検知を報告します。例:
foo='123
bar=baz
=xyz'
コードを実行します。 (特に)foo=
、bar=
および=
を出力します。これを修正するには、そのような変数が実際に存在するかどうかを各行でテストする必要があります。
または、重複を取得することもできます。
foo='123
aaa foo=456
bar=baz
bar=qux'
これを修正するには、sort -u
を使用できます。私は最後に小文字の変数を取得するためにLC_COLLATE=POSIX sort -u
を好みます。
上記のfoo
は、エクスポートされた場合、正当な(エクスポートされない)bar
を「マスク」できます。例:
export foo
bar=999
現在、コードはbar=
を出力しません。これを修正するには、「グローバル」declare -x -p
に基づくことを除外しないでください。各変数について、エクスポートされていないかどうかを個別に確認する必要があります。この手順により、予備のgrep -vxF
が不要になります。
したがって、この:
declare -p | grep -o '[^ =]*=' | LC_COLLATE=POSIX sort -u \
| while IFS='=' read -r v ; do
[ -v "$v" ] \
&& bash -c "[ ! -v '$v' ]" \
&& printf '%s\n' "$v"
done
注変数がエクスポートされるかどうかは厳密にはチェックしません。子に設定されているかどうかを確認しますbash
。レポートされるインタラクティブシェルに固有の変数があります。手動で除外することもできます。または、bash -ic …
を実行することもできます。しかし、複数回なので、さらに遅くなります。
誤検知なしですべての変数のリストを取得する信頼できる(?)方法があるため、grep -vxF
の元のアイデアに戻り、子bash
を1回だけ実行できます。次のスニペットは、bash -i
で非常にうまく機能します。私の場合、手動で除外する変数はCOLUMNS
とLINES
のみです。
( set +x
function _get_vars {
declare -p | grep -o '[^ =]*=' | LC_COLLATE=POSIX sort -u \
| while IFS='=' read -r v ; do
[ -v "$v" ] \
&& printf '%s\n' "$v"
done
}
export -f _get_vars
_get_vars | grep -vxF -f <( bash -ic '_get_vars' ) \
| grep -vE '^(COLUMNS|LINES)$' )
念のため、サブシェルには意図的にset +x
を使用しています。サブシェルは、関数を「ローカル」に保つのにも役立ちます(現在のシェルでは使用できません)。非インタラクティブな子bashとより多くのフィルタリングを好むかもしれません。これを必要に応じて調整してください。
たぶん、適切な場所にset +x
を置くだけでcompgen
アプローチがうまくいくでしょう。これはどう?
( set +x
compgen -A variable | grep -vxF -f <( bash -ic 'compgen -A variable' ) \
| grep -vE '^(COLUMNS|LINES)$' )
免責事項:Bash 3.2.52ではテストされていません。 4.4.12でテスト済み。
ニーズに応じて、1つの簡単な解決策は次のようになります:
set > tmp
_または_set > /tmp/current
_を使用して、変更前の現在の変数を保存しますdiff -u <(cat tmp) <(set)
で変更されたものを抽出する、または_diff -u <(cat tmp) <(set) | tail -n +3 | grep -v PIPESTATUS | grep -v "_=" | grep -E "^\+"
_
どこ:
_tail -n +3
_:diffコマンドの最初の不要な行を削除します
_grep -v PIPESTATUS | grep -v "_="
_:不要な変数を削除
_grep -E "^\+"
_:必要な/必要な変更のみを表示
代替:
_diff -u <(set -o posix; set) <(bash -lc 'set -o posix; set') | grep -E "^\+"
_
注:
あなたのスクリプトでは、exec
が完全に置き換えられることに注意してください。これから起動されるプロセスは、達成しようとしていることに干渉する可能性があります
すべての自己宣言変数(すべての非環境変数)をエコーしたい
これは、私にとって、comm
のタスクです。
comm -23 <( set | cut -d= -f1 | sort ) <( env | cut -d= -f1 | sort )
-23
は、comm
が列2および3の印刷を省略すべきであることを意味します。
3番目は両方のリストにあるエントリで、2番目はenv
の後処理出力にのみあるエントリです。
または、IOW 最初のリストにあった一意のエントリのみを表示。
P. S.一部のエントリは両方のリストで同時に見つけることができるため(comm -12 …
)、あなたcouldもそれらを結果セットに含めたいです。つまり、-3
その場合、3列目からの行は印刷時にシフトして表示されます。