web-dev-qa-db-ja.com

ディレクトリのすべてのファイルでタブをスペースに変換する方法を教えてください。

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

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

222
Cynede

警告:これはリポジトリを壊すでしょう。

svn.gitの下にあるものも含めて、これはバイナリファイルを破壊するでしょう!使用する前にコメントを読んでください。

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

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

欠点:

  • ファイル内のあらゆる場所でタブを置き換えます。
  • このディレクトリに5GBのSQLダンプがあるとしたら、時間がかかるでしょう。
63
Martin Beckett

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

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

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

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

323
Gene

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

expand -i -t 4 input | sponge output

どこで

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

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

179
kev

Gene's answer から最良のコメントを収集する、これまでのところ最良の解決策は、 moreutils からspongeを使用することです。

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はすべてのTABを4つのスペースに拡張します
  • spongeは(expandから)標準入力を取り込み、ファイル(同じもの)に書き込みます*。

NOTE:*単純なファイルリダイレクト(> "$0")はここでは動作しません。 ファイルがすぐに上書きされてしまう

利点:元のファイルのアクセス許可はすべて保持され、中間のtmpファイルは使用されません。

20
not2qubit

バックスラッシュでエスケープされた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
    
16
e9t

再帰的アプリケーションのための上の "find"の例が好きです。ワイルドカードに一致する現在のディレクトリ内のファイルを変更するだけで、再帰的でないようにそれを適応させるために、シェルグロブ展開は少量のファイルに対して十分であることができます:

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

それが機能すると信頼した後にそれを静かにしたければ、最後にshコマンドに-vをドロップするだけです。

もちろん、最初のコマンドで任意のファイルセットを選ぶことができます。たとえば、特定のサブディレクトリ(複数可)のみを、次のように制御された方法でリストします。

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
4
drchuck

一般的に入手可能な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. タブが文字列リテラルに埋め込まれているコードファイルがある場合、これは適切な解決策ではありません。
4
codeforester

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

これは通常、not欲しいものです。

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

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

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

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

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

少し前に sanitize_files という小さなスクリプトを作成しました。また、\r\n\nで置き換える、末尾の\nなどを追加するなど、その他の一般的な問題も修正します。

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

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


#!/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 __== '__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)
4
Martin Tournoij

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

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

findtabs-to-spaces パッケージと一緒に使うことができます。

最初にtabs-to-spacesをインストールしてください

npm install -g tabs-to-spaces

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

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

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

2
Harsh Vakharia

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

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

#!/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
daka

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

2
Theo Belaire

私のお勧めは、

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

コメント:

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

そのためにvimを使うことができます。

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

Carpetsmokerが述べたように、それはあなたのvimの設定に従って再配置されます。そして、もしあればファイル内のモデル化します。また、行の先頭だけでなくタブも置き換えられます。これはあなたが一般的に欲しいものではありません。たとえば、タブを含むリテラルがあるかもしれません。

1
x-yuri

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 残念ながらI 簡潔なPOSIXの代替方法 が見つかりません。

あなたのコードベースが文字列で機能的な生のタブを許可するという狂った考えを持っていたならば、:

expand -i

次に、行頭以外のすべてのタブを1つずつ試してみてください。 grepのタブをgitすることはできますか?

Ubuntu 18.04でテスト済み。

誰もrplを言及していませんか? rplを使うと任意の文字列を置き換えることができます。タブをスペースに変換するには、

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

とても簡単です。

他の回答で提案されているように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
1
John B