275

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

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

4

19 に答える 19

369

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

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

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

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

于 2012-06-19T04:58:54.800 に答える
208

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

expand -i -t 4 input | sponge output

どこ

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

最後に、 Homebrew()またはMacPorts( )gexpandでインストールcoreutilsした後、macOSで使用できます。brew install coreutilssudo port install coreutils

于 2012-06-19T04:33:49.653 に答える
72

警告:これにより、リポジトリが破損します。

これにより、、!の下にあるものを含むバイナリファイルが破損します。使用する前にコメントを読んでください!svn.git

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

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

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

欠点:

  • ファイル内のあらゆる場所のタブを置き換えます。
  • このディレクトリに5GBのSQLダンプがある場合は、長い時間がかかります。
于 2012-06-19T04:35:01.260 に答える
43

Geneの回答から最高のコメントを収集することは、これまでで最高の解決策であり、frommoreutilsspongeを使用することです。

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)を吸収し、ファイル(同じもの)に書き込みます*。

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

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

于 2017-04-20T15:08:29.040 に答える
22

バックスラッシュでエスケープされたを使用します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
    
于 2015-10-29T22:33:13.293 に答える
10

一般的に利用可能な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. 文字列リテラルに裸のタブが埋め込まれているコードファイルがある場合、これは適切な解決策ではありません
于 2017-05-04T05:47:40.190 に答える
5

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

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

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

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

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
于 2014-04-29T15:31:17.957 に答える
5

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

これは通常、あなたが望むものではありません。

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

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

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

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

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

少し前に、 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)
于 2015-08-12T14:11:14.000 に答える
5

私の推奨事項は、以下を使用することです。

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

コメント:

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

findこのためのtabs-to-spacesパッケージで使用できます。

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

npm install -g tabs-to-spaces

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

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

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

于 2017-01-12T09:08:50.537 に答える
4

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

于 2013-09-29T15:54:56.317 に答える
4

そのために使用vimすることができます:

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

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

于 2014-11-10T08:28:29.177 に答える
4

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

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
于 2016-06-29T16:05:27.223 に答える
3

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

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

#!/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;
于 2017-01-31T14:12:40.210 に答える
3

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の代替手段が見つかりません

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

expand -i

次に、すべての非開始行タブを1つずつ確認して、リストに追加できます。タブのgrepをgitすることは可能ですか?

Ubuntu18.04でテスト済み。

于 2018-09-02T11:21:53.250 に答える
3

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

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

とてもシンプルです。

于 2019-03-22T01:08:15.990 に答える
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
于 2015-03-31T01:50:26.597 に答える
-1

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

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;
于 2013-10-26T23:52:48.740 に答える
-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 $*
}
于 2015-04-19T22:58:48.680 に答える