ディレクトリのすべてのファイルでタブをスペースに変換するにはどうすればよいですか?


251

ディレクトリのすべてのファイルでタブをスペースに変換するにはどうすればよいですか(おそらく再帰的に)。

また、タブごとのスペースの数を設定する方法はありますか?


ファイルまたはファイル名のタブを置き換えたいですか?
cppcoder 2012年

3
prこれは素晴らしいユーティリティです。この回答を参照してください。
codeforester 2017年

回答:


69

警告:これはリポジトリを壊します。

この意志破損したバイナリファイルを、それらの下を含め、svn.git!使用する前にコメントを読んでください!

find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +

元のファイルはとして保存され[filename].origます。

「* .java」を、探しているファイルタイプの末尾で置き換えます。これにより、バイナリファイルの偶発的な破損を防ぐことができます。

欠点:

  • ファイル内のすべてのタブを置き換えます。
  • このディレクトリに5GBのSQLダンプがあると、長い時間がかかります。

12
タブとスペースが混在するビジュアルスペースの場合、このアプローチでは正しく展開されません。
ピザ

7
また、たとえば.phpファイルのみに./ -iname "* .php" -type f -exec sed -i 's / \ t / / g' {} \;のようなファイルマッチャーを追加します。
Daniel Luca CleanUnicorn 2013年

98
SEDは使用しないでください。文字列に埋め込まれたタブがある場合、コードを変更する可能性があります。これは、expandコマンドが処理するためのものでした。を使用しexpandます。
David W.

5
@DavidW。このコマンドを更新して、行の最初からタブのみを置き換えるようにします。find ./ -type f -exec sed -i 's/^\t/####/g' {} \;。しかし、私はエキスパンドコマンドを認識していませんでした-とても便利です!
Martin Konecny、2014年

29
使ってはいけません!この答えも、私のローカルgitリポジトリを破壊しました。タブとスペースが混在しているファイルがある場合は、#のシーケンスが挿入されます。代わりに、Geneによる回答またはDogeによるコメントを使用してください。
人形

344

での単純な置き換えsedは問題ありませんが、最善の解決策ではありません。タブの間に「余分な」スペースがある場合、タブは置換後も存在するため、マージンは不揃いになります。行の途中で展開されたタブも正しく機能しません。ではbash、代わりに

find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;

expand現在のディレクトリツリー内のすべてのJavaファイルに適用します。-name他のファイルタイプをターゲットにしている場合は、引数を削除または置き換えます。コメントの1つが言及しているように-name、弱いワイルドカードを削除または使用するときは十分注意してください。意図せずに、リポジトリやその他の隠しファイルを簡単に上書きできます。これが、元の回答にこれが含まれていた理由です。

何か問題が発生した場合に備えて、このようなことを試みる前に、必ずツリーのバックアップコピーを作成してください。


2
@JeffreyMartinez素晴らしい質問です。11月11日にgniourf_gniourfが私の元の回答を編集し、適切な使用方法がわからないことについて中傷的な発言をしました{}。彼は$0いつ-c使用されるのか知りませんでした。次に、dimo414は、変換ディレクトリでのtempの使用からに変更されました/tmp。これ/tmpは、が別のマウントポイントにある場合、はるかに遅くなります。残念ながら、私はあなたの$0提案をテストするために利用できるLinuxボックスを持っていません。しかし、私はあなたが正しいと思います。
Gene

1
@Gene、明確化に感謝します。stackoverflowalright:pのようです。ただし、その間は、*。javaを適切にエスケープするために、 '*。java'を引用符で囲む必要があったことを付け加えておきます。
ジェフリーマルティネス

2
誰もが検索から「原発不明またはオペレータのエラーを持っている場合は、ここでそれを修正する完全なコマンドは次のとおりです。find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
元首

4
使用使用している場合:私は、そう、これは私のものであり、それがあったとして、この答えは十分にコメントがないと思っていたspongeからjoeyh.name/code/moreutilsは、あなたが書くことができますfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
tokland

8
愚かで使用しないでください。find . -name '*'ローカルのgitレポを破壊しただけです
Gautam

193

コマンドラインツールをお試しくださいexpand

expand -i -t 4 input | sponge output

どこ

  • -i 各行の先頭のタブのみを展開するために使用されます。
  • -t 4 各タブが4つの空白文字(デフォルトでは8)に変換されることを意味します。
  • spongemoreutilsパッケージからのものであり、入力ファイルの消去を回避します

最後に、Homebrew()でgexpandインストールcoreutilsした後、OSXで使用できますbrew install coreutils


5
kev

32
に渡し-iexpand、各行の先頭のタブのみを置き換える必要があります。これにより、コードの一部である可能性のあるタブの置き換えを回避できます。
Quolonelの質問2014

10
ディレクトリ内のすべてのファイルについて再帰的にはどうですか?
ahnbizcad

4
これを使おうとするたびに、ファイルの一部(通常はすべて)が空白になります。:\
ThorSummoner 2015年

5
@ThorSummoner:bash inputと同じファイルの場合、output開始する前にコンテンツを上書きしますexpand。これがどのように>機能するかです。
Robert Siemer、2015

34

Geneの回答から最高のコメントを収集することは、これまでで最高のソリューションでありspongemoreutilsを使用することです

sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;

説明:

  • ./ 現在のディレクトリから再帰的に検索しています
  • -iname大文字と小文字を区別しない一致です(両方*.java*.JAVAいいね!)
  • type -f 通常のファイルのみを検索します(ディレクトリ、バイナリ、シンボリックリンクは検索しません)
  • -exec bash -c ファイル名ごとにサブシェルで次のコマンドを実行します。 {}
  • expand -t 4 すべてのタブを4つのスペースに拡張します
  • sponge(からのexpand)標準入力を吸収し、ファイル(同じもの)に書き込みます*。

:*単純なファイルリダイレクト(> "$0")は、ファイルをすぐに上書きするためここでは機能しません。

利点:すべての元のファイル権限が保持され、中間tmpファイルは使用されません。


2
TIL:Linuxを15年間使用して以来、素晴らしいスポンジコマンド。インターネットから神秘的な騎士に感謝します。
sscarduzio 2017年

19

バックスラッシュエスケープを使用しますsed

Linuxの場合:

  • すべての* .txtファイルで、すべてのタブを1つのハイフンインプレースに置き換えます。

    sed -i $'s/\t/-/g' *.txt
  • すべての* .txtファイルで、すべてのタブを1つのスペースインプレースに置き換えます。

    sed -i $'s/\t/ /g' *.txt
  • すべての* .txtファイルで、すべてのタブを4つのスペースに置き換えます。

    sed -i $'s/\t/    /g' *.txt

Macの場合:

  • すべての* .txtファイルで、すべてのタブを4つのスペースに置き換えます。

    sed -i '' $'s/\t/    /g' *.txt

2
Маша@sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
xyzale

この答えは最も単純なようです。
Yan King Yin

6

一般的に利用可能なprコマンドを使用できます(manページはこちら)。たとえば、タブを4つのスペースに変換するには、次のようにします。

pr -t -e=4 file > file.expanded
  • -t ヘッダーを抑制します
  • -e=numタブをnumスペースに展開します

バイナリファイルをスキップしながら、ディレクトリツリー内のすべてのファイルを再帰的に変換するには:

#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
  [[ -f "$f" ]]   || continue # skip if not a regular file
  ! grep -qI "$f" && continue # skip binary files
  pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done

バイナリファイルをスキップするロジックは、この投稿にあります。

注意:

  1. これを行うと、gitまたはsvnリポジトリで危険な場合があります
  2. 文字列リテラルにタブが埋め込まれているコードファイルがある場合、これは適切なソリューションではありません。

1
expandどちらもPOSIXであることを考えると、何か利点はありますか?たとえば、インライン変更オプションはありますか?Gitの安全性:stackoverflow.com/a/52136507/895245
Ciro Santilli郝海东冠状病六四事件法轮機能

5

ディレクトリのすべてのファイルでタブをスペースに変換するにはどうすればよいですか(おそらく再帰的に)。

これは通常はありませんあなたが欲しいもの。

これをpng画像に実行しますか?PDFファイル?.gitディレクトリ?あなたの Makefile(タブが必要です)?5GB SQLダンプ?

理論的には、exludeオプションの多くを、使用しfindている他のオプションに渡すことができます。しかし、これは壊れやすく、他のバイナリファイルを追加するとすぐに壊れます。

あなたが欲しいのは少なくとも:

  1. 特定のサイズを超えるファイルをスキップします。
  2. NULLバイトの存在をチェックして、ファイルがバイナリかどうかを検出します。
  3. ファイルの先頭にあるタブのみを置き換えます(expandこれはsed 行いますが、行いません)。

私の知る限り、これを実行できる「標準」のUNIXユーティリティはなく、シェルの1ライナーを使用して実行するのは簡単ではないため、スクリプトが必要です。

少し前に、まさにそれを行うsanitize_filesと呼ばれる小さなスクリプトを作成しました 。また、交換のようないくつかの他の一般的なものを修正\r\nして\n、末尾に追加する\nなど、

以下の追加機能やコマンドライン引数なしで簡略化されたスクリプト見つけることができますが、バグ修正やこの投稿よりも更新されたものを受け取る可能性が高いため、上記のスクリプトを使用することをお勧めします。

私はまた、のようなシェルグロブを使用していることを、ここでは他の回答の一部に応じて、指摘するのでしょうではないに収まるよりも遅かれ早かれ、あなたが複数のファイルになってしまいますので、これを行うための堅牢な方法ARG_MAX近代的に( Linuxシステムは128kであり、多くのように見えるかもしれませんが、遅かれ早かれ 十分ではありません)。


#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#

import os, re, sys


def is_binary(data):
    return data.find(b'\000') >= 0


def should_ignore(path):
    keep = [
        # VCS systems
        '.git/', '.hg/' '.svn/' 'CVS/',

        # These files have significant whitespace/tabs, and cannot be edited
        # safely
        # TODO: there are probably more of these files..
        'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
    ]

    for k in keep:
        if '/%s' % k in path:
            return True
    return False


def run(files):
    indent_find = b'\t'
    indent_replace = b'    ' * indent_width

    for f in files:
        if should_ignore(f):
            print('Ignoring %s' % f)
            continue

        try:
            size = os.stat(f).st_size
        # Unresolvable symlink, just ignore those
        except FileNotFoundError as exc:
            print('%s is unresolvable, skipping (%s)' % (f, exc))
            continue

        if size == 0: continue
        if size > 1024 ** 2:
            print("Skipping `%s' because it's over 1MiB" % f)
            continue

        try:
            data = open(f, 'rb').read()
        except (OSError, PermissionError) as exc:
            print("Error: Unable to read `%s': %s" % (f, exc))
            continue

        if is_binary(data):
            print("Skipping `%s' because it looks binary" % f)
            continue

        data = data.split(b'\n')

        fixed_indent = False
        for i, line in enumerate(data):
            # Fix indentation
            repl_count = 0
            while line.startswith(indent_find):
                fixed_indent = True
                repl_count += 1
                line = line.replace(indent_find, b'', 1)

            if repl_count > 0:
                line = indent_replace * repl_count + line

        data = list(filter(lambda x: x is not None, data))

        try:
            open(f, 'wb').write(b'\n'.join(data))
        except (OSError, PermissionError) as exc:
            print("Error: Unable to write to `%s': %s" % (f, exc))


if __name__ == '__main__':
    allfiles = []
    for root, dirs, files in os.walk(os.getcwd()):
        for f in files:
            p = '%s/%s' % (root, f)
            if do_add:
                allfiles.append(p)

    run(allfiles)

gitの中では、バイナリチェックは簡単です:stackoverflow.com/a/52136507/895245
チロSantilli郝海东冠状病六四事件法轮功

5

上記の再帰的なアプリケーションの「検索」の例が好きです。これを非再帰的で、ワイルドカードに一致する現在のディレクトリ内のファイルのみを変更するように適合させるには、シェルグロブ拡張で少量のファイルを十分に処理できます。

ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v

動作すると信頼した後でサイレントにしたい場合-vは、shコマンドのonを最後にドロップしてください。

もちろん、最初のコマンドでファイルのセットを選択できます。たとえば、次のように制御された方法で特定のサブディレクトリのみを一覧表示します。

ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

または、深さパラメータなどを組み合わせてfind(1)を実行します。

find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

1
ファイル名の合計のARG_MAX長さは長さのみであるため、シェルグロビングは遅かれ早かれ中断します。これはLinuxシステムでは128kですが、シェルグロビングに依存しないようにするのに十分な回数この制限に遭遇しました。
Martin Tournoij、2015

1
それらを実際に適応させる必要はありません。find語らすることができ-maxdepth 1、そしてそれだけで変更されているディレクトリのエントリではなく、ツリー全体を処理します。
ShadowRanger、2015年

4

私が使用astyle混合タブとスペースを見つけた後に再インデントすべての私のC / C ++コードに。必要に応じて、特定のブレーススタイルを強制するオプションもあります。


4

そのために使用できますvim

find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;

Carpetsmokerが述べたように、それはあなたのvim設定に従って再タブ化します。そして、もしあればファイルのモードライン。また、行の最初だけでなくタブも置き換えます。これは一般的には望んでいないことです。たとえば、タブを含むリテラルがあるとします。


:retab最初のタブではなく、ファイル内のすべてのタブを変更します。また、vimrcやモードラインでの設定:tabstop:expandtab設定に依存するため、これがまったく機能しない場合があります。
Martin Tournoij、2015

@Carpetsmoker行頭のタブに関する良い点。ここでのソリューションのいずれかがこのケースを処理しますか?tabstopexpandtab設定については、を使用している場合はうまくいきますvim。ファイルにモード行がない場合。
x-yuri 2015

@ x-yuriいい質問ですが、一般的には議論の余地があります。ほとんどの人は、\ t実際のタブではなくリテラルを使用します。
Ricardo Cruz

4

私の推奨は、使用することです:

find . -name '*.lua' -exec ex '+%s/\t/  /g' -cwq {} \;

コメント:

  1. インプレース編集を使用します。VCSにバックアップを保存します。* .origファイルを作成する必要はありません。結果を前回のコミットと比較して、これが予想どおりに機能することを確認することをお勧めします。
  2. sedストリームエディタです。exインプレース編集に使用します。これにより、トップの回答にあるように、余分な一時ファイルを作成したり、置換ごとにシェルを生成したりする必要がなくなります
  3. 警告:これは、インデントに使用されるタブだけでなく、すべてのタブを混乱させます。また、タブのコンテキスト対応の置換も行いません。これは私のユースケースでは十分でした。しかし、あなたには受け入れられないかもしれません。
  4. 編集:のfind|xargs代わりに使用されたこの回答の以前のバージョンfind -exec。@ gniourf-gniourfで指摘されているように、これはファイル名cfのスペース、引用符、制御文字に関する問題を引き起こします。ウィーラー

exすべてのUnixシステムで利用できるとは限りません。で置き換えると、vi -eより多くのマシンで動作する可能性があります。また、正規表現は、任意の数の開始タブ文字を2つのスペースに置き換えます。+%s/\t/ /gマルチレベルのインデントを破壊しないように正規表現を置き換えます。ただし、これはインデントに使用されないタブ文字にも影響します。
Lukas Schmelzeisen

exはPOSIX [1]の一部なので、利用できるはずです。マルチレベルインデンテーションの良い点。私は実際/\t/ /にファイルでバリアントを使用していましたが、/\t\+//インデントしないタブを壊さないことを選択しました。マルチインデントの問題を見逃しました!回答の更新。[1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
Heinrich Hartmann

2
xargsこの方法での使用は、役に立たず、非効率的で、壊れています(スペースまたは引用符を含むファイル名を考えてください)。代わりにfind-execスイッチを使用しないのはなぜですか?
gniourf_gniourf 2016年

スペースと引用符を含むファイル名は壊れていると私は主張します。)あなたがそれをサポートする必要があるなら、私は選ぶでしょう:-print0/ xargsを見つけるためのオプション。-exec以来、私はxargsが好きです。
ハインリッヒハートマン2016年

@gniourf_gniourfコメントを追加して更新しました。
ハインリッヒハートマン2016年

4

タブの代わりに4つのスペースを使用するようにディレクトリ内のすべてのJavaファイルを再帰的に変換するには:

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;

この回答は、4年前に投稿されたthisとどう違うのですか?
PP

2
あなたの答えもそうです。実際、これはGeneの回答の下位バージョンです。1)Geneの回答は、同じ名前のディレクトリを処理します。2)展開に失敗した場合は移動しません。
PP

4

あなたは使うことができfindtabs-to-spacesこれにパッケージで。

まず、インストール tabs-to-spaces

npm install -g tabs-to-spaces

次に、プロジェクトのルートディレクトリからこのコマンドを実行します。

find . -name '*' -exec t2s --spaces 2 {} \;

これにより、すべてのファイルですべてのtab文字が2 spacesに置き換えられます。


3

ボディは言及されていませんrplか?rplを使用すると、任意の文字列を置き換えることができます。タブをスペースに変換するには、

rpl -R -e "\t" "    "  .

とてもシンプル。


1
これにより、私のリポジトリ内のすべてのバイナリファイルが破損しました。
アーロンフランケ

1
優れたコマンドですが、上で指定した再帰的なフォルダー内のすべてのファイルオプションでは潜在的に危険です。--dry-runオプションを「念のため」追加して、正しいフォルダーに座っていることを確認します。
MortimerCat

2

expand他の回答で提案されているようにを使用することは、このタスクだけで最も論理的なアプローチのようです。

とは言っても、BashとAwkを使用して、他の変更を加えたい場合に備えて行うこともできます。

Bash 4.0以降を使用している場合、組み込みshoptをglobstar使用して、で再帰的に検索できます**

GNU Awkバージョン4.1以降では、「インプレース」のようなsedファイル変更を行うことができます。

shopt -s globstar
gawk -i inplace '{gsub("\t","    ")}1' **/*.ext

タブごとにスペースの数を設定する場合:

gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext

2

次のスクリプトをダウンロードして実行し、プレーンテキストファイルのハードタブをソフトタブに再帰的に変換します。

プレーンテキストファイルを含むフォルダー内からスクリプトを実行します。

#!/bin/bash

find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
    echo "Converting... "$file"";
    data=$(expand --initial -t 4 "$file");
    rm "$file";
    echo "$data" > "$file";
}; done;

2

Gitリポジトリに適した方法

git-tab-to-space() (
  d="$(mktemp -d)"
  git grep --cached -Il '' | grep -E "${1:-.}" | \
    xargs -I'{}' bash -c '\
    f="${1}/f" \
    && expand -t 4 "$0" > "$f" && \
    chmod --reference="$0" "$f" && \
    mv "$f" "$0"' \
    '{}' "$d" \
  ;
  rmdir "$d"
)

現在のディレクトリの下にあるすべてのファイルを操作します。

git-tab-to-space

CまたはC ++ファイルに対してのみ機能します。

git-tab-to-space '\.(c|h)(|pp)$'

タブを必要とする迷惑なMakefileがあるため、特にこれが必要になるでしょう。

コマンドgit grep --cached -Il ''

  • 追跡されたファイルのみを一覧表示するため、内部には何もありません .git
  • ディレクトリ、バイナリファイル(破損する可能性があります)、シンボリックリンク(通常のファイルに変換されます)を除外します

で説明されているように: gitリポジトリ内のすべてのテキスト(非バイナリ)ファイルを一覧表示する方法は?

chmod --referenceファイルのアクセス許可を変更せずに維持します:https : //unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file残念ながら私は簡潔POSIXの選択肢を見つけることができません

コードベースに文字列で機能的な生のタブを許可するというクレイジーなアイデアがある場合は、以下を使用します。

expand -i

次に、行の先頭以外のすべてのタブを1つずつ確認していきます。これを次のように表示できます。タブのgit grepを実行できますか?

Ubuntu 18.04でテスト済み。


-1

".lua"ファイル内のタブをスペースに変換する[タブ-> 2つのスペース]

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;

明らかに、タブが展開されるスペースの量は、コンテキストによって異なります。したがって、sedはタスクに完全に不適切なツールです。
2015年

?? @Sven、私のsedコマンドは、expandコマンドと同じことを行います(expand -t 4 input >output
Makah

3
もちろん違います。expand -t 4タブがa\tb3スペースに、タブがaa\tb2スペースに拡張されます。expandタブのコンテキストを考慮に入れますが、コンテキストにsed関係なく、指定したスペースの量でタブを置き換えません。
2015年

-1

vim-wayを使用します。

$ ex +'bufdo retab' -cxa **/*.*
  • バックアップしてください!上記のコマンドを実行する前に、バイナリファイルが破損する可能性があります。
  • 再帰にglobstar**)を使用するには、によってアクティブ化しshopt -s globstarます。
  • 特定のファイルタイプを指定するには、例を使用します**/*.c

タブストップを変更するには、を追加し+'set ts=2'ます。

ただし、欠点は、文字列内のタブ置き換えることができることです。

したがって、(置換を使用して)少し良い解決策を得るには、以下を試してください。

$ ex -s +'bufdo %s/^\t\+/  /ge' -cxa **/*.*

または、exエディタ+ expandユーティリティを使用して:

$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*

末尾のスペースについては、複数のファイルの末尾の空白を削除する方法を参照してください


次の関数をに追加できます.bash_profile

# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
  ex +'set ts=2' +'bufdo retab' -cxa $*
}

:私はちょうどあなた;-)理由は、このスレッドでは、多くの回答をないdownvoted :retabていない可能性のあるすべての作業をシェルグロブは、この種のもののための悪いソリューションで、あなたの:sコマンドは置き換えられます任意のあなたの2つのスペースとタブの量を(そのほとんど:!expandプロセスを実行するためだけにexを開始するのは馬鹿げています...
Martin Tournoij、2015

...そして、すべてのソリューションはバイナリファイルなど(.pngファイル、.pdfファイルなど)を
上書きし

これは率直に言ってドキュメントに対する恐ろしい提案です。これを理解するには、いくつかのプログラムのかなり不透明な構文とセマンティックスの問題に精通している必要があります。
Josip Rodin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.