ファイルの先頭に追加する 1 つのライナーをシェル化する

StackOverflow https://stackoverflow.com/questions/54365

  •  09-06-2019
  •  | 
  •  

質問

これはおそらく 複雑な解決策.

「>>」のような単純な演算子を、先頭に追加するために探しています。

残念ながら存在しません。次のようなことをしなければなりません

 mv myfile tmp
 cat myheader tmp > myfile

もっと賢いことはありますか?

役に立ちましたか?

解決

ハック 以下は即席の簡単な回答で、うまく機能し、多くの賛成票を獲得しました。その後、この質問の人気が高まり、時間が経つにつれて、憤慨した人々が、それは多少は機能するが、奇妙なことが起こる可能性がある、またはまったく機能しないと報告し始めたため、一時は猛烈な反対票を投じられました。とても楽しいです。

このソリューションは、システム上のファイル記述子の正確な実装を利用します。実装は nix 間で大幅に異なるため、その成功は完全にシステムに依存しており、移植性は決定的になく、漠然と重要なものであっても依存すべきではありません。

さて、以上を述べましたが、答えは次のとおりです。


ファイル用に別のファイル記述子を作成します (exec 3<> yourfile)だからそれに書きます(>&3) は、同じファイルの読み取り/書き込みのジレンマを克服しているようです。私にとっては、awk を使用した 600K ファイルで機能します。ただし、「cat」を使用して同じトリックを試みると失敗します。

プリペンデージを変数として awk に渡します (-v TEXT="$text") 'sed' でこのトリックを行うのを妨げるリテラルの引用符の問題を解決します。

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

他のヒント

これでも一時ファイルを使用しますが、少なくとも 1 行に記述されています。

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

クレジット: バッシュ:ファイルの先頭にテキスト/行を追加する

echo '0a
your text here
.
w' | ed some_file

ed は標準エディターです! http://www.gnu.org/fun/jokes/ed.msg.html

ジョン・ミー:あなたのメソッドが動作することは保証されておらず、4096 バイトを超えるものを先頭に追加するとおそらく失敗します (少なくとも gnu awk ではそうなりますが、他の実装にも同様の制約があると思います)。この場合、失敗するだけでなく、自身の出力を読み取る無限ループに入り、利用可能なスペースがすべて埋まるまでファイルが大きくなります。

自分で試してみてください:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(警告:しばらくしてから強制終了しないと、ファイルシステムがいっぱいになってしまいます)

さらに、そのようにファイルを編集するのは非常に危険であり、非常に悪いアドバイスです。ファイルの編集中に何か(クラッシュ、ディスクがいっぱい)が発生した場合、ファイルはほぼ確実に不整合な状態のままになるためです。

一時ファイルがないと不可能ですが、ワンライナーを記載します

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

ed や perl などの他のツールを使用すると、一時ファイルを使用せずにこれを行うことができます。

多くの場合、 良いアイデア 次のようなユーティリティを使用して一時ファイルを安全に生成するには mktemp, 少なくともスクリプトが root 権限で実行される場合には。たとえば、次のことを実行できます (これも bash で)。

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )

あなたが管理するコンピュータでこれが必要な場合は、パッケージ「moreutils」をインストールし、「sponge」を使用してください。次に、次のことができます。

cat header myfile | sponge myfile

bash ヒアドキュメントを使用すると、tmp ファイルの必要性を回避できます。

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

これが機能するのは、bash スクリプトの評価時に、リダイレクト付きの cat が実行される前に $(cat myfile) が評価されるためです。

編集したいファイルが my.txt であると仮定します。

$cat my.txt    
this is the regular file

そして、先頭に追加したいファイルはヘッダーです

$ cat header
this is the header

ヘッダー ファイルには必ず最後の空白行を入れてください。
これで、先頭に次の文字を追加できます

$cat header <(cat my.txt) > my.txt

結局のところ、

$ cat my.txt
this is the header
this is the regular file

私の知る限り、これは「bash」でのみ機能します。

シェルスクリプトでは困難なことを実行しようとしている場合は、「適切な」スクリプト言語 (Python/Perl/Ruby/など) でスクリプトを書き直すことを検討することを強くお勧めします。

ファイルの先頭に行を追加する場合、次のようなことを行う場合のように、パイプを介してこれを行うことはできません。 cat blah.txt | grep something > blah.txt, 、誤ってファイルを空にしてしまいます。という小さなユーティリティコマンドがあります。 sponge インストールできます(インストールできます) cat blah.txt | grep something | sponge blah.txt ファイルの内容をバッファリングしてからファイルに書き込みます)。これは一時ファイルに似ていますが、明示的に行う必要はありません。しかし、それは、たとえば Perl よりも「悪い」要件だと思います。

awk などを介して行う方法もあるかもしれませんが、シェル スクリプトを使用する必要がある場合は、一時ファイルを使用するのが最も簡単な (/唯一の?) 方法だと思います。

編集:これは壊れています。見る ファイルの先頭に cat と tee を追加すると、奇妙な動作が発生する

上書き問題の回避策は次のとおりです。 tee:

cat header main | tee main > /dev/null

Daniel Velkov が提案しているように、T シャツを使用してください。
私にとって、それはシンプルで賢い解決策です。

{ echo foo; cat bar; } | tee bar > /dev/null

私が使っているもの。これにより、順序や追加の文字などを好きな方法で指定できます。

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

追伸:ファイルにバックスラッシュを含むテキストが含まれている場合は機能しません。エスケープ文字として解釈されるためです。

主に楽しみ/シェルゴルフのためですが、

ex -c '0r myheader|x' myfile

パイプラインやリダイレクトはありません。もちろん、vi/ex は実際には非対話型の使用を目的としたものではないため、vi は短時間点滅します。

単純に ed コマンドを使用してみてはいかがでしょうか (fuffle によってすでに提案されているように)。

ed はファイル全体をメモリに読み取り、自動的にその場でファイル編集を実行します。

したがって、ファイルがそれほど大きくない場合は...

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

さらに別の回避策は、Jürgen Hötzel が提案したように、オープン ファイル ハンドルを使用することです。 出力を sed 's/c/d/' myFile から myFile にリダイレクトします

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

もちろん、これらすべてを 1 行にまとめることができます。

固定テキストを先頭に追加する「一時ファイルなし」に対する cb0 のソリューションのバリエーション:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

これも、cat が入力と出力に同じファイルを持つことを拒否するのを避けるために、サブシェルの実行 (..) に依存しています。

注記:このソリューションが気に入りました。ただし、私の Mac では元のファイルが失われています (失われるべきではないと思いましたが、失われます)。これは、次のようにソリューションを作成することで修正できます。エコー「テキストを準備するテキスト」| cat -file_to_be_modified | cat> tmp_file;mv tmp_file 変更されるファイル

私が発見したことは次のとおりです。

echo -e "header \n$(cat file)" >file
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile

警告:OP のニーズを満たすにはもう少し作業が必要です。

@shixilun 氏の懸念にもかかわらず、sed アプローチを機能させる方法はあるはずです。ファイルを sed 置換文字列に読み取るときに空白をエスケープする bash コマンドが必要です (例:改行文字を「 」に置き換えます。シェルコマンド vis そして cat 印刷不可能な文字は処理できますが、空白は処理できないため、これではOPの問題は解決されません。

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

代替スクリプト内の生の改行が原因で失敗します。シェルと sed を正常に保つために、先頭に行継続文字 () を追加し、おそらくその後に & を続ける必要があります。 このSOの答え

sed 非グローバル検索置換コマンド (パターンの後に /g が続くことはない) には 40K のサイズ制限があるため、匿名が警告した awk の恐ろしいバッファ オーバーラン問題を回避できる可能性があります。

sed -i -e "1s/^/new first line\n/" old_file.txt

$( コマンド ) コマンドの出力を変数に書き込むことができます。そこで、一時ファイルを使用せず、1 行の 3 つのコマンドで実行しました。

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile

大きなファイル (私の場合は数百キロバイト) があり、Python にアクセスする場合、これは Python よりもはるかに高速です。 cat パイプソリューション:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

による解決策 printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

次のこともできます:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

ただし、その場合は、何もないことを確認する必要があります % ターゲット ファイルの内容を含め、どこにでも保存できます。解釈されて結果が台無しになる可能性があります。

Perl コマンドラインを使用できます。

perl -i -0777 -pe 's/^/my_header/' tmp

ここで、-iはファイルのインライン置換を作成し、-0777はファイル全体を丸lurpし、 ^ ^の始まりのみを一致させます。-pe はすべての行を出力します

または、my_header がファイルの場合:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

/e を使用すると、置換でのコードの評価が可能になります。

current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

ここで、「my_file」は「my_string」を先頭に追加するファイルです。

気に入っています @フラッフルさん アプローチ 最高の。結局のところ、ここでは、ツールのコマンド ライン スイッチとスクリプト エディタ コマンドは本質的に同じものです。スクリプト化されたエディター ソリューションの「クリーンさ」が劣っているかどうかはわかりません。

これが私のワンライナーに追加されたものです .git/hooks/prepare-commit-msg リポジトリ内を先頭に追加するには .gitmessage メッセージをコミットするファイル:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

.gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

私がやっている 1r の代わりに 0r, これにより、元のテンプレートのファイルの先頭に空の書き込み可能行が残るためです。先頭に空行を置かないでください。 .gitmessage そうすると、空の行が 2 行できてしまいます。 -s ed の診断情報の出力を抑制します。

これを経験することに関連して、私は 発見した vim マニアにとっては、次のものがあると良いでしょう。

[core]
        editor = vim -c ':normal gg'

変数、ふーん?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list

これは ed の最もクリーンなバリエーションだと思います。

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

関数として:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile

BASH でスクリプトを作成している場合は、実際には次のように発行するだけです。

cat - yourfile  /tmp/out && mv /tmp/out yourfile

これは実際に、あなた自身が自分の質問に投稿した複雑な例にあります。

私の意見では、2 つのファイルのサイズに関係なく、一貫して確実に動作するシェル ソリューションはありません (そして、決してそうなることはありません)。 myheader そして myfile. 。その理由は、一時ファイルを再帰せずに (そして、シェルに一時ファイルをサイレントに再帰させずに) それを実行したい場合です。のような構造を通じて exec 3<>myfile, 、への配管 tee, 、など。

あなたが探している「実際の」ソリューションはファイルシステムをいじる必要があるため、ユーザー空間では利用できず、プラットフォームに依存します。が使用しているファイルシステムポインタを変更するよう求めています。 myfile ファイルシステムポインタの現在の値へ myheader ファイルシステム内で置き換えます EOFmyheader が指す現在のファイルシステムアドレスへの連鎖リンクを使用します。 myfile. 。これは簡単なことではなく、明らかにスーパーユーザー以外には実行できませんし、おそらくスーパーユーザーにも実行できないでしょう...inode などをいじってみます。

ただし、ループ デバイスを使用すると、これを多かれ少なかれ偽装できます。たとえば、を参照してください このSOスレッド.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top