ループ内の特殊文字が他のコマンドの引数にエスケープされるのを防ぐ方法は?

ループ内の特殊文字が他のコマンドの引数にエスケープされるのを防ぐ方法は?

私の入力ファイル(objects.lst)は次のとおりです。

host.fqdn.com:/ 'host.fqdn.com [/]'
host.fqdn.com:/boot 'host.fqdn.com [/boot]'
host.fqdn.com:/tmp 'host.fqdn.com [/tmp]'
host.fqdn.com:/tmp '/tmp'
host.fqdn.com:/ '/usr/share'
host.fqdn.com:/var 'host.fqdn.com [/var]'

このファイルを1行ずつ繰り返したいと思い、各行はサードパーティ製ソフトウェアの引数として必要です。

だから私は次のことを試しました。

#!/bin/bash
set -x
while read -r line; do
/opt/report -filesystem "$line" -detail
done < objects.lst

これは次のように解釈されます。

 /opt/report -filesystem 'host.fqdn.com:/ '\''host.fqdn.com [/]'\''' -detail

しかし、そうしなければなりません。

/opt/report -filesystem host.fqdn.com:/ 'host.fqdn.com [/]'

パラメータを配列、変数、さまざまな参照に保存し、forループを介して保存するなど、さまざまな操作を試しましたが、echo -E望む方法で動作することはありません。

現在の解決策は次のとおりです。

#!/bin/bash
set -x
while read -r line; do
echo "/opt/report -filesystem "${line}" -detail" | bash -
done < objects.lst

bashにエコー/パイピングなしで動作させる別の方法はありますか?

${parameter@Q}(bash 4.4で利用可能)の例をいくつか見ましたが、bash 4.2を使用しています。

明らかにset -x出力を混乱させましたが、問題はまだ同じです。私はobject.lstを繰り返しており、object.lstとまったく同じ行全体がパラメータとして必要です。

したがって、以下の説明を正しく理解すると、次のようになります。

while read -r line; do
/opt/report -filesystem "$line" -detail
done < objects.lst

これは正確に次のようになります。

/opt/report -filesystem host.fqdn.com:/ 'host.fqdn.com [/]'

しかしそれは真実ではない。ループ内でomnidb / reportで「オブジェクトが見つかりません」が発生するため、サードパーティ製のソフトウェアはここで問題になる可能性があります(microfocus data saver / omnidb cliコマンド)。

しかし、私が作るなら

while read -r line; do
echo "/opt/report -filesystem "$line" -detail"
done < objects.lst

出力コマンドからコピー/貼り付けを行うと正常に動作し、omnidb/report コマンドで正しいオブジェクト情報を印刷します。

答え1

の場合、/opt/report -filesystem "$line" -detailhost.fqdn.com:/ 'host.fqdn.com [/]'のようにそのまま渡されます。一つコマンドの引数と出力は、set -x値を一重引用符(シェルではリテラル引用符)として表示することでこれを通知します。

存在する

/opt/report -filesystem host.fqdn.com:/ 'host.fqdn.com [/]'

コマンドに2つの引数(host.fqdn.com:/および)を渡しますhost.fqdn.com [/]。これは、空白文字と一重引用符文字の両方がシェル構文で特殊であるためです。

シェルの場合、上記の'...'ように文字通り引用符は文字通りテキストを渡すために使用され、内部文字は特に処理されず、シェル構文のSPC(自己が引用符で囲まれていない場合)は引数(またはより一般的にはシェル構文のトークン)を区切るために使用されます。

host.fqdn.com:/ 'host.fqdn.com [/]'出力に示すように文字通り引数として渡すには、set -x次のようなものが必要です'host.fqdn.com:/ '\''host.fqdn.com [/]'\'''。ただし、他の種類の引用符を使用して"host.fqdn.com:/ 'host.fqdn.com [/]'"も機能します。

$ printf '<%s>\n' 'host.fqdn.com:/ '\''host.fqdn.com [/]'\'''
<host.fqdn.com:/ 'host.fqdn.com [/]'>
$ printf '<%s>\n' "host.fqdn.com:/ 'host.fqdn.com [/]'"
<host.fqdn.com:/ 'host.fqdn.com [/]'>

これをコマンドに渡されたリテラル引数ではなくシェルコードとして解釈する必要がある場合は、パイプのようなものからシェルインタプリタを呼び出すhost.fqdn.com:/ 'host.fqdn.com [/]'必要があります(二重引用符の使用は正しくありませんが)。$linebasheval

#!/bin/bash -
set -x
while IFS= read -r line; do
  eval "/opt/report -filesystem $line -detail"
done < objects.lst

ただし、これは行に;reboot;含まれている場合、またはそれが含まれている場合は$(reboot)コマンドrebootが呼び出されることを意味します。

他の副作用(上記のコマンドの実行など)なしでシェルトークン化と参照の削除を実行したい場合は、演算子でコマンドをreboot使用できます。zsh

#!/bin/zsh -
set -x
while IFS= read -r line; do
  /opt/report -filesystem "${(Q@)${(z)line}}" -detail
done < objects.lst

これzによりトークン化がzshコードになり、Q引用符レイヤーが削除されます。たとえば、次のような行から:

foo 'bar baz' $(echo x y)

foobar bazおよび3つのパラメータを渡します$(echo x y)

答え2

作成した内容によると、その行を単一の引数としてプログラムに渡す必要はありませんが、2つの引数として渡す必要があるようです。行の引用符で囲まれていない部分を1つに、次の引用符付き文字列を別の引数に渡します。 。

もしこれらの行はすべてこの形式に従います(引用符で囲まれた文字列を含む行はありません)。 Bashでは、次のことができます。

#/bin/bash
while IFS=" " read -r a b; do
    b=${b%\'};
    b=${b#\'};
    /opt/report -filesystem "$a" "$b" -detail
done < objects.lst

最初のスペースでは、行を2つの部分に分割し、その部分をsumreadに保存します。その後、一重引用符が削除され、両方の文字列が別の引数として渡されます。abb=${b%\'}; b=${b#\'};/opt/report

関連情報