bash
コマンドの交換によって末尾の改行文字が削除されるのを防ぐためのオプションが提供されていれば幸いです。あなたはいますか?そうでなければ、POSIXのようなシェルはありますかbash
?標準(POSIXのようです)sh
コードを実行するシェルはありますか?そのようなオプションがあるか、改行文字を削除しない特別なコマンド代替構文があるかもしれません(後者がより良い)。 )?
答え1
(私が知っている限り)これを直接実行するオプションはありませんが、コマンドの置き換えに改行ではなく保護文字を追加してから削除して偽にすることができます。
var="$(somecommand; echo .)" # Add a "." (and one newline that command
# substitution will remove) *after* the
# newline(s) we want to protect
var="${var%.}" # Remove the "." from the end
との終了状態は両方とも状態が設定されているsomecommand
ため(通常0)、プロセスで失われます。最後まで保存する必要がある場合は、ジャグリングを追加する必要があります(Kusalananda提供)。echo .
var=${var%.}
var="$(somecommand; err=$?; echo .; exit "$err")" # Make the subshell
# exit with somecommand's
# status
commandstatus=$? # Preserve the exit status of the subshell
var="${var%.}"
# You can now test $commandstatus to see if the command succeeded
err
と変数はすべてcommandstatus
コマンドの終了状態を格納しますが、err
によって生成されたサブシェルにのみ存在し、親$( )
シェルcommandstatus
にのみ存在するため、重複しません。両方に同じ名前を使用できますが、これは混乱を引き起こす可能性があります。
さて、「。」後ろに状態を保存する必要がない場合は、切り捨ててこのcommandstatus
部分をスキップできます。
if var="$(somecommand; err=$?; echo .; exit "$err")"; then
# somecommand succeeded; proceed with processing
var="${var%.}"
dosomethingwith "$var"
else
echo "somecommand failed with exit status $?" >&2
fi
ロケールに応じて「。」代わりに、どの文字も使用できません。 POSIXは「。」エンコーディングが他の文字エンコーディングで見つからないことを保証しますが、すべての場合に当てはまるわけではありません。たとえば、実際には「x」のエンコーディングは、BIG5またはGB18030エンコーディングの多くの文字のエンコーディングの終わりにあるため、「。」代わりに「x」を追加すると、「x」は次のように終了できます。出力の終わりに現れる他のバイトの組み合わせは新しい文字を形成し、 "${var%x}" は "x" を削除できません。
答え2
私が知っている限り、コマンド置換機能があり、末尾の改行文字を削除しないように指示できる唯一のシェルはrc
(Unix V10とplan9のシェル、Byron Rakitzisのパブリックドメインの複製、es
およびその派生akanga
)ですfish
。
rc
シェルのような状況では、
var = `{somecommand}
$ifs
出力で区切られた単語をsomecommand
変数に格納します$var
(変数はの配列/リストですrc
)。
空の場合、$ifs
(ifs = ()
)出力はそのまま保存されます。区切り文字を指定することもできます``(separators){somecommand}
。つまり、次のようになります。
var = ``(){somecommand}
しかし、rc
// The Bourne Supremacyとはまったく異なりますes
。akanga
また、一般的に削除したい場合一つ改行文字。
たとえば、
dirname=$(dirname -- "$file")
追加した改行を削除したいが、dirname
ディレクトリの末尾にある可能性のある改行は削除したくありません。$file
rc
組み込み演算子はありません。以下を行う必要があります。
dirname = ``(){dirname -- $file | head -c -1}
(標準にはありませんhead
)。パターン抽出演算子をes
使用できます。~~
nl = '
'
dirname = <={ ~~ ``(){dirname -- $file} *$nl }
これはほとんど明確ではありません(たとえ関数として作成できますが)。
シェルfish
コマンド置換は、出力を対応するコンポーネント行に分割します。
set var (somecommand)
出力ラインをsomecommand
配列に保存します$var
(出力の末尾にある空のラインも保持されます)。
空のリストまたは空の文字列に設定すると、その分割が$IFS
無効になり、最大1つの改行が出力の末尾から削除されます(少なくともfish
現在のバージョンのIIRCでは、これに関していくつかの変更があります。年)。だからそこに、
begin; set -l IFS; set dir (dirname -- $file); end
信頼できます。
私は皮をむかないように言うことができるBourneのような皮を知らない。みんな末尾の改行文字。この動作はPOSIXで必要であり、Bourneに似たすべてのシェルはposixモードでない場合でもこれを行います(bashやzshなどのシェルを使用する場合)。
4.4+では、コマンド置換と一緒にプロセス置換を使用することもできます(またはbash
非対話型インスタンスとreadarray
オプション付きパイプ)。lastpipe
readarray -td '' var < <(somecommand)
これは-d ''
NULの分割です。bash
いずれにせよ、変数にNULを格納することはサポートされていないので、コマンド置換の場合は置換だけで十分です。
readarray -t lines < <(somecommand)
出力行を配列somecommand
に保存します。$lines
その後、改行文字で連結して出力を再構成できます。一つ末尾改行:
IFS=$'\n'
output="${lines[*]}"
ただし、これらすべての方法では終了ステータスが失われますsomecommand
。
コマンドの置き換えによって削除された誤った機能を解決するにはみんな改行文字の一般的な慣用語は、改行文字ではなく文字を追加してそれを削除することです(そして一つ改行) Gordonが言ったように。
ただし、現実的な地位を失うことなくこれを行うには、次のようにします。
cmdsubst() { # args: var cmd args
eval "$1"'=$(shift; "$@"; ret=$?; printf .; exit "$ret")'
eval "$1=\${$1%.}; $1=\${$1%'
'}; return $?"
}
これはコマンド置換と似ています。一つ改行文字が削除されます。
次のように使用されます。
cmdsubst dir dirname -- "$file"
変える:
dir=$(dirname -- "$file")
またはより複雑なコマンドの場合:
cmdsubst var eval 'cmd1; for i in a b; do cmd "$i"; done'
答え3
多くのユースケースで機能する簡単なアプローチは、コマンドの置換を二重引用符で囲み、閉じる引用符の直前に改行文字を挿入することです。改行文字を配置して~へ引用するが外部括弧を使用すると、コマンド置換範囲からそれらを削除し、シェルはそれを自動的に削除しません。
最初の例では、閉じる引用符の前に改行文字がないため、次の$
プロンプトは出力直後に表示されます。 2番目の例はうまくいきます。
$ printf '%s' "$(date)"
Mon Jan 22 13:39:23 PST 2024$ printf '%s' "$(date)
"
Mon Jan 22 13:39:26 PST 2024
$
この方法には、シェルが括弧で囲まれたコマンド置換から末尾の改行を削除し、その後スクリプトが1つの改行のみを追加するために残るという制限があります。単一末尾の改行文字。改行文字が複数ある場合は、1 つに圧縮されます。