ディレクトリからすべてのファイルの最後の行を削除する方法は?


17

ディレクトリに多くのテキストファイルがあり、ディレクトリ内のすべてのファイルの最後の行を削除したいです。

どうすればいいですか?


6
何を試しましたか?unix.stackexchange.com/help/how-to-ask:「あなたの研究を共有することはみんなに役立ちます。あなたが見つけたものとそれがあなたのニーズに合わなかった理由を教えてください。 、明白な答えを繰り返してしまうことから私たちを救い、何よりも、より具体的で関連性の高い答えを得るのに役立ちます!」
パトリック

誰かが誤って/ etcにそれを適用するという考えが
クリンジ

回答:


4

にアクセスできる場合はvim、次を使用できます。

for file in ./*
do
  if [ -f "${file}" ]
  then
    vim -c '$d' -c "wq" "${file}"
  fi
done

16

GNUをお持ちの場合、この素敵なonelinerを使用できますsed

 sed -i '$ d' ./*

現在のディレクトリにある隠されていない各ファイルの最後の行を削除します。-iGNU用のスイッチはsed、所定の場所で動作することを意味し、最後の行を削除するように'$ d'指示しますsed$最後をd意味し、削除を意味します)。


3
フォルダは、通常のファイル以外のもの、例えば別のフォルダ...含まれている場合、これはエラーを投げる(と何もしない)だろう
ナジブIdrissi

1
あなたが使用している@StefanR -iこれは議論の余地があるので、GNUismであるが、私はいくつかの古いバージョンがあることを指摘して失敗した場合、私は私の古い髭が立っ失うことになるsedあなたは間の空白を置くことを許可していない$d(または、一般に、パターンとコマンドの間)。
-zwol

1
@zwol私が書いたように、これは警告ではなくエラーとなり、sedはそのファイルに到達するとonceめます(少なくとも私が持っているsedのバージョンでは)。次のファイルは処理されません。エラーメッセージを捨てることは、あなたがそれが起こったことを知らないので、ひどい考えです!zsh *(.)では、通常のファイルのグロブに使用できますが、他のシェルについては知りません。
ナジブイドリッシ

@NajibIdrissiうーん、あなたは正しい。びっくりしました。ディレクトリについて文句を言うことを期待していましたが、コマンドラインの次のファイルに進みます。実際、それをバグとして報告するつもりです。
-zwol

@don_crissti私もGNU sed v4.3を持っています...何を伝えるべきかわかりません。もう一度テストしました。gist.github.com/nidrissi/66fad6be334234f5dbb41c539d84d61e
Najib Idrissi

11

ディレクトリに通常のファイル、またはファイル名にスペース/改行が含まれるファイル以外のものが含まれている場合、他の回答にはすべて問題があります。関係なく動作するものを次に示します。

find "$dir" -type f -exec sed -i '$d' '{}' '+'
  • find "$dir" -type f:ディレクトリ内のファイルを見つける $dir
    • -type f これは通常のファイルです。
    • -exec 見つかった各ファイルでコマンドを実行します
    • sed -i:ファイルを適切に編集します。
    • '$d'd最後の($)行を削除()します。
    • '+':findに引数を追加し続けるように指示しますsed(@zwolのおかげで、各ファイルに対してコマンドを個別に実行するよりも少し効率的です)。

サブディレクトリに降りたくない場合は、引数-maxdepth 1をに追加できますfind


1
うーん、しかし、他の答えとは異なり、これはサブディレクトリに降ります。(また、findこれの現在のバージョンではより効率的に記述されていfind $dir -type f -exec sed -i '$d' '{}' '+'ます。)
zwol

@zwolありがとう、答えにそれを追加しました。
ナジブイドリッシ

-print0完全なコマンドに存在しないのに、なぜそれを説明に入れるのですか?
ルスラン

1
また、-depth 0機能しません(findutils 4.4.2)-maxdepth 1。代わりにが必要です。
ルスラン

@Ruslan最初のバージョンではxargsを使用していましたが、覚えていました-exec
ナジブイドリッシ

9

GNUを使用sed -i '$d'すると、ファイル全体を読み取り、最終行なしでコピーを作成することを意味しますが、(少なくとも大きなファイルの場合)ファイルを所定の位置に切り捨てる方がはるかに効率的です。

GNU truncateでは、次のことができます。

for file in ./*; do
  [ -f "$file" ] &&
    length=$(tail -n 1 "$file" | wc -c) &&
    [ "$length" -gt 0 ] &&
    truncate -s "-$length" "$file"
done

ファイルが比較的小さい場合、ファイルごとに複数のコマンドを実行するため、おそらく効率は低下します。

最後の改行文字の後(最後の行の後)に余分なバイトを含むファイル、または区切りのない最後のがある場合、tail実装に応じて、tail -n 1それらの余分なバイトのみを返します(GNUなどtail)または最後の(適切に区切られた)行とそれらの余分なバイト。


通話|wc -c中に必要tailですか?(またはa ${#length}
ジェフシャラー

@JeffSchaller。おっとっと。wc -cは実際に意図されていました。${#length}バイトで$(...)はなく文字をカウントし、末尾の改行文字を削除するため、機能しません${#...}すべての文字がシングルバイトであっても1つずれます。
ステファンシャゼル

6

よりポータブルなアプローチ:

for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done

私はこれに説明が必要だとは思わない...多分この場合dは同じであることを除いて$dので、edデフォルトは最後の行を選択することによって。
これは再帰的に検索せず、隠しファイル(別名ドットファイル)を処理しません。
これらも編集する場合は、ディレクトリ内の隠しファイルと*を一致させる方法を参照してください。


いいね!に変更[[]]する[]と、完全にPOSIX準拠になります。([[ ... ]]バシズムです。)
ワイルドカード

@Wildcard-ありがとう、変更されました([[バシズムではありませんが
-don_crissti

非POSIX主義です。:)
ワイルドカード

3

ドットファイルを含む現在のディレクトリで再帰的に開始するすべてのファイル用のPOSIX準拠のワンライナー:

find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

以下のため.txtのファイルのみ、非再帰的に:

find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

参照:

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.