web-dev-qa-db-ja.com

変数をsedに渡す際の問題

変数をsedに渡すときは、二重引用符を使用する必要があることを知っています。したがって、sedを使用して、行番号を指定してファイルの行を表示すると、次のようになります。

sed '894!d' cloud.cpp

動作しますが、行番号894を変数としてsedに渡したいので、次のように試みました。

lnum=894
sed "$lnum!d" cloud.cpp

しかし、これは機能せず、同じ構文の単一引用符でも機能しません。

sed "894!d" cloud.cpp

したがって、この場合、どのようにして変数をsedに渡すことができますか?.

4

既に述べたように、文字列に単一引用符を使用すると、その内容は文字どおりに扱われるため、二重引用符を使用する必要があります。しかし、あなたの場合、パターンと変数の組み合わせがあります。したがって、パターンには一重引用符を使用し、変数には二重引用符を使用する必要があります。

sed "$lnum"'!d' cloud.cpp
5
Dababi

変数に数字のみが含まれている場合、行ったことは正しいです。

sed "$lnum!d" cloud.cpp

一般に、シェル変数をsedに渡すには、 この答え をお読みください。


これで、この場合は正しく実行できましたが、それでも機能しません。それはおそらくbashzshまたはcshのシェルによるものです。これらのシェルは、!で示される履歴拡張をサポートします(この機能はcshからオリジナルであり、bashおよびzshによって後でコピーされます)。

bashzshでは、履歴の拡張はインタラクティブセッションでデフォルトで有効になっており、二重引用符内で実行されます。

zsh内:

$ seq 10 | sed "10!d" # <-- press enter
$ seq 10 | sed "10dash"

dashは、dで始まる履歴内の最新のコマンドであるためです。

bash内:

$ seq 10 | sed "10!d"
bash: !d": event not found

bashの履歴にはコマンドがないため、dで始まります。

履歴の拡張をオフにすることができます:

set +H

または:

set +o histexpand

bashおよび

set -K

または:

setopt nohistexpand

zshにあります。


より移植性の高い方法では、シェルオプションに依存せずに、いつでも次のことができます。

sed "$lnum"\!d cloud.cpp

これにより、シェルは!を文字通り処理します。

8
cuonglm

使用しているシェルについては触れませんでした。以下の答えは、それがbashであることを前提としています。


シェルで履歴の拡張をトリガーしています。 man bashから:

!string

文字列で始まる履歴リストの現在位置の前にある最新のコマンドを参照してください。

それについて奇妙なことはそれです:

有効にすると、二重引用符で囲まれた!がバックスラッシュを使用してエスケープされない限り、履歴の展開が実行されます。 !の前のバックスラッシュは削除されません。

したがって、私はそれらを別々に引用することがおそらく最良の解決策だと思います:

sed "$lnum"'!d' cloud.cpp

これも機能します(charを保存します)。

sed "$lnum"\!d cloud.cpp

これも機能します(二重引用符を使用する場合)。

sed "$lnum""!""d" cloud.cpp
4
Cyker