bash置換を使用して文字列からエスケープされたリテラル記号をすべて取得する方法

bash置換を使用して文字列からエスケープされたリテラル記号をすべて取得する方法

現在実行中の文字列からエスケープされたリテラルのみを返すことができるようにしたいです。

foo="\'\"\(foobar\)'another'[program]\[\$var\]()"
echo "${foo//[^\\\']/}"

ただし、次のように出力されます。

\'\\''\\

必要な出力は次のようになります。

\'\"\(\)\[\$\]

私はまだ文字通りの一重引用符を取得しようとしている段階にありますが、どういうわけか動作しないか、bash拡張で実際に可能ですか?

編集する

文字列はbash $ READLINE_LINEから来るので、二重引用符の三重バックスラッシュなどの追加のエスケープはありません。

答え1

変数をリテラル値に設定

\'\"\(foobar\)'another'[program]\[\$var\]()

二重引用符で囲まれた文字列を使用すると、各リテラルバックスラッシュをエスケープする必要があります。そしてそれ以外の場合は、各二重引用符またはドル記号が拡張をトリガーします。

string="\\'\\\"\\(foobar\\)'another'[program]\\[\\\$var\\]()"

一重引用符で囲まれた文字列を使用する場合は、一重引用符の挿入にのみ注意してください。

string='\'"'"'\"\(foobar\)'"'"'another'"'"'[program]\[\$var\]()'

ここでは、一重引用符ごとに二重引用符を一重引用符に追加し、一重引用符文字列を分割することを選択しました'"'"'。一重引用符で囲まれた文字列の外からエスケープされた一重引用符を使用することもできます'\''

引用が面倒すぎる場合は、ここで参照文書を使用することもできます。

string=$( cat <<'END'
\'\"\(foobar\)'another'[program]\[\$var\]()
END
)

改行文字が文字列の最後の文字である場合、末尾の改行文字は切り捨てられます。

その後、コードが試みます。削除すべてのバックスラッシュと一重引用符は正しくないようです。代わりにいくつかのツールを使用してください。タブレット\そして次の文字のすべてのインスタンス:

grep -o '\\.' <<<"$string"

これは生産します

\'
\"
\(
\)
\[
\$
\]

または、

grep -o '\\.' <<<"$string" | paste -s -d '\0' -

質問の出力を正確に再現します。

bashループから直接これを実行することもできます。

while [[ $string =~ \\. ]]; do
    printf '%s\n' "${BASH_REMATCH[0]}"
    string=${string#*\\?}
done

または、

while [[ $string =~ '\'. ]]; do
    printf '%s\n' "${BASH_REMATCH[0]}"
    string=${string#*'\'?}
done

stringこれらの一連の文字が文字列に存在するたびに、バックスラッシュなどの文字の次の一致ビットまで切り捨てられ、値が変更されます。各反復において、与えられた正規表現に一致するビットが印刷される。

答え2

zsh代わりにを使用すると、bash次のことができます。

set -o extendedglob
print -r -- ${foo//(#b)((\\?)|?)/$match[2]}

または以下を使用してksh93

print -r -- "${foo//@(@(\\?)|@(?))/\2}"

(それもうまくいくべきだと思いますprint -r -- "${foo//@(@(\\?)|?)/\2}"が、そうではありません。間違い)

そしてfish

string join '' (string match -ar '\\\\.' $foo)

答え3

現在の文字を削除するかどうかを知るには、前の文字を調べる必要があるため、パターン置換操作を使用してこれらの文字列を見つけることはできないと思います。これを行うには、Perlの正規表現の否定的なLookBehindのようなものが必要です。\\x最初のバックスラッシュは2番目のバックスラッシュをエスケープする必要がありますが、2番目のバックスラッシュはそうではありませんx。少なくとも定義が一般的に機能する範囲内でバックスラッシュがエスケープされる場合、このような文字列を考慮するとはるかに困難です。

ループ内で一致する部分を見つけるのは簡単ですが、Bashはこれを非常に簡単にします。 (正規表現一致演算子があり、一致[[ text =~ re ]]項目を で見つけることができますが、${BASH_REMATCH[@]}手動以外の複数のヒットを繰り返す方法はないと思います。)

ただし、たとえば、grep次のようにこれを行うことができます。たとえば、次は一行に 1 つずつ一連の一致を出力します。

foo="\'\\\"\(foobar\)'another'[program]\[\$var\]()"
echo "$foo" | grep -oe '\\.'

その後、出力をパイプしてtr -d '\n'改行文字を削除します。または、シェルで処理する必要がある場合は使用してください。while IFS= read -r line; do...ただし、そうするには別のツールを使用する必要があります。シェルはテキスト処理には適していません。

関連情報