[bash] 디렉토리의 모든 파일에서 탭을 공백으로 변환하는 방법은 무엇입니까?

디렉토리의 모든 파일에서 탭을 공백으로 변환하려면 어떻게해야합니까 (재귀 적으로)?

또한 탭당 공백 수를 설정하는 방법이 있습니까?



답변

경고 : 이렇게하면 저장소가 손상됩니다.

손상됩니다 바이너리 파일 들에서 포함 svn, .git! 사용하기 전에 의견을 읽으십시오!

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

원본 파일은로 저장됩니다 [filename].orig.

‘* .java’를 찾고있는 파일 유형의 파일 끝으로 바꾸십시오. 이렇게하면 실수로 이진 파일이 손상되는 것을 방지 할 수 있습니다.

단점 :

  • 파일의 모든 곳에서 탭을 대체합니다.
  • 이 디렉토리에 5GB SQL 덤프가 있으면 시간이 오래 걸립니다.

답변

간단한 교체 sed는 가능하지만 최상의 솔루션은 아닙니다. 탭 사이에 “추가”공간이 있으면 교체 후에도 여백이 그대로 유지되므로 여백이 고르지 않게됩니다. 줄 중간에 확장 된 탭도 제대로 작동하지 않습니다. 에서 bash대신 말할 수 있습니다.

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

expand현재 디렉토리 트리의 모든 Java 파일에 적용 합니다. -name다른 파일 형식을 대상으로하는 경우 인수를 제거 / 바꾸십시오 . 주석 중 하나에서 언급했듯이 -name약한 와일드 카드를 제거 하거나 사용할 때 매우주의하십시오 . 의도없이 리포지토리 및 기타 숨겨진 파일을 쉽게 클로버 할 수 있습니다. 이것이 원래 답변에 다음이 포함 된 이유입니다.

무언가 잘못 될 경우를 대비하여 이와 같은 것을 시도하기 전에 항상 트리의 백업 사본을 만들어야합니다.


답변

명령 행 도구를 사용해보십시오 expand.

expand -i -t 4 input | sponge output

어디

마지막으로 Homebrew ( )로 gexpand설치 한 후 OSX에서 사용할 수 있습니다 .coreutilsbrew install coreutils


답변

지금까지 최고의 솔루션 인 Gene의 답변 에서 최고의 의견을 수집하는 것은 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)을 흡수하고 파일 (같은 파일)에 씁니다. *.

참고 : * 간단한 파일 리디렉션 ( > "$0")은 파일을 너무 빨리 덮어 쓰기 때문에 여기서 작동하지 않습니다 .

장점 : 모든 원본 파일 권한이 유지되고 중간 tmp파일이 사용 되지 않습니다 .


답변

백 슬래시로 이스케이프 처리하십시오 sed.

리눅스에서 :

  • 모든 * .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

답변

일반적으로 사용 가능한 pr명령 (man page here )을 사용할 수 있습니다 . 예를 들어, 탭을 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. 이것을하는 것은 자식이나 svn repo에서 위험 할 수 있습니다
  2. 문자열 리터럴에 탭이 포함 된 코드 파일이있는 경우 올바른 솔루션이 아닙니다.

답변

디렉토리의 모든 파일에서 탭을 공백으로 변환하려면 어떻게해야합니까 (재귀 적으로)?

이것은 일반적으로 원하는 것이 아닙니다 .

png 이미지에 대해이 작업을 수행 하시겠습니까? PDF 파일? .git 디렉토리? 당신의
Makefile( 필요한 탭 합니까)? 5GB SQL 덤프?

이론적으로는 많은 exlude 옵션을 전달할 수 있습니다. find 당신이 사용하는 다른 . 그러나 이것은 깨지기 쉽고 다른 바이너리 파일을 추가하자마자 깨질 것입니다.

당신이 원하는 것은 최소한 :

  1. 특정 크기 이상의 파일을 건너 뜁니다.
  2. NULL 바이트가 있는지 확인하여 파일이 이진인지 감지합니다.
  3. 파일 의 시작 부분 에서만 탭을 교체 하십시오 ( expand그렇지 만 sed
    그렇지 않습니다).

내가 아는 한,이 작업을 수행 할 수있는 “표준”유닉스 유틸리티가 없으며, 쉘 one-liner로 수행하기가 쉽지 않으므로 스크립트가 필요합니다.

얼마 전에 나는 정확히 그렇게하는 sanitize_files 라는 작은 스크립트를 만들었습니다
. 또한로 대체 , 후행 추가 \r\n와 같은 다른 일반적인 사항을 수정합니다.\n\n

아래에 추가 기능과 명령 줄 인수가 없는 간단한 스크립트 찾을 수 있지만이 스크립트는 버그 수정 및이 게시물 이외의 다른 업데이트를받을 가능성이 높으므로 위의 스크립트를 사용하는 것이 좋습니다.

쉘 대체 (globbing)을 사용하는 것을 나는 또한처럼, 여기에 다른 몇 가지 답변에 대한 응답으로 지적하는 것 아니 이 일을 강력한 방법은 조만간 당신이 맞는 것보다 더 많은 파일로 끝날 것이기 때문에, 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)