bash 스크립트를 사용하여 파일 이름의 공백을 바꾸는 방법


264

주어진 루트 디렉토리에서 시작하여 파일 및 디렉토리 이름에서 공백을 밑줄로 재귀 적으로 대체하는 안전한 솔루션을 추천 할 수 있습니까? 예를 들면 다음과 같습니다.

$ tree
.
|-- a dir
|   `-- file with spaces.txt
`-- b dir
    |-- another file with spaces.txt
    `-- yet another file with spaces.pdf

된다 :

$ tree
.
|-- a_dir
|   `-- file_with_spaces.txt
`-- b_dir
    |-- another_file_with_spaces.txt
    `-- yet_another_file_with_spaces.pdf

9
같은 디렉토리에 foo bar파일이 있고 다른 파일 이 있으면 foo_bar어떻게됩니까?
Mark Byers

좋은 질문. 기존 파일을 덮어 쓰거나 데이터를 잃고 싶지 않습니다. 변경하지 않은 채로 두어야합니다. 이상적으로 경고를 인쇄하지만 너무 많이 묻습니다.
armandino

답변:


311

시스템에 이미있을 수있는 Perl 스크립트 인 rename(aka prename)를 사용하십시오 . 두 단계로 수행하십시오.

find -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find -name "* *" -type f | rename 's/ /_/g'

Jürgen의 답변을 바탕으로 " /usr/bin/renamePerl 스크립트 "의 "Revision 1.5 1998/12/18 16:16:31 rmb1"버전을 사용하여 단일 계층에서 여러 계층의 파일과 디렉토리를 처리 할 수 ​​있음

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;

5
두 단계가 필요하지 않음 : 깊이 우선 검색 사용 : dir -depth
Jürgen Hötzel 찾기

3
아, 난 그냥 읽은 rename맨 페이지를 (I 도구를 몰랐다) 그리고 당신이 변경하여 코드를 최적화 할 수 있습니다 생각 s/ /_/gy/ /_/;-)
해커 - 마이클 Krelin을

1
물론 성능 향상을 얻지 못할 것입니다. 올바른 도구를 사용하는 것에 대한 자세한 내용입니다. 그리고이 모든 질문은 어느 정도의 미세 최적화에 관한 것입니다. 결국 재미 있지 않습니까? ;-)
Michael Krelin-해커

14
당신이 OS X에서이 작업을 실행하는 경우, 당신은해야합니다brew install rename
loeschg

7
이름 바꾸기 명령이 완전히 다르기 때문에 (Perl 스크립트가 아닌 이진 파일이므로) Centos 7에서는 작동하지 않으며 stdin의 데이터를 허용하지 않습니다.
CpnCrunch

357

나는 사용한다:

for f in *\ *; do mv "$f" "${f// /_}"; done

재귀 적이지는 않지만 매우 빠르고 간단합니다. 여기 누군가가 재귀 적으로 업데이트 할 수 있다고 확신합니다.

${f// /_}부분 문자열 공급 파라미터 내에 패턴을 교체 떠들썩한 파티 파라미터의 팽창기구를 이용한다. 관련 구문은 ${parameter/pattern/string}입니다. 참조 : https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html 또는 http://wiki.bash-hackers.org/syntax/pe .


9
간단하고 Mac에서 작동합니다. (mac에는을 가지고 있지 rename않으며, 이것을 brew와 함께 설치하기가 너무 어렵습니다 ..)
JonnieJS

6
멋진 답변입니다. 나는 for d in *\ *; do mv "$d" "${d// /}"; done점수 미달을 사용했다 .
Yoon Lee

1
'find-name'답변과 달리 이것은 OS X에서 작동했습니다! 감사합니다!
Julio Faerman

1
참고로,이 쉽게 할 수 있습니다 하게 사용하는 bash는 재귀 shopt -s globstarfor f in **/*\ *; do .... 이 globstar옵션은 bash 내부에 있지만 rename명령은 일반적인 Linux 도구이며 bash의 일부는 아닙니다.
ghoti

2
${f// /_}A는 배쉬의 변수 확장 에 대한 검색 및 교체는 . - 는 공백이 포함 된 각 파일에 f대한 for루프 의 변수입니다 . -첫 번째 //는 "모두 바꾸기"를 의미합니다 (처음에 멈추지 마십시오). -`/ _`는 "공간을 밑줄로 대체"를 의미합니다
Ari

101
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done

디렉토리를 생각하지 않았기 때문에 처음에는 제대로하지 못했습니다.


1
매력처럼 작동합니다. 고마워 마이클.
armandino

데니스, 풍어 쉽게 넣어 고정 IFS=''앞에 read. 또한 다른 의견으로 말할 sort수있는 것에 대해서는 -depth옵션을 선호하여 단계를 삭제할 수 있습니다 find.
Michael Krelin-해커

1
파일 이름에 \ (백 슬래시)가 포함되어 있으면 작동하지 않습니다 . -r읽을 옵션을 추가하여 수정할 수 있습니다 .
jfg956

8
솔루션을 복사하고 사용하려면이 페이지를 방문한 50 번째 시간이어야합니다. 감사합니다 아주 많이 . 나는 Mac에 있고 renameDennis가 제안한 명령 이 없으므로 귀하의 답변을 선호합니다 .
Alex Constantin

@AlexConstantin,하지 않는 macportsrename? 나는 그 임무가 유틸리티를 정당화한다고 생각하지 않기 때문에 결코 알아 내지 않았다. 그리고 macports설치되어 있지 않다면 설치를 고려해야합니다.)
Michael Krelin-해커

30

detoxDoug Harple에서 사용할 수 있습니다

detox -r <folder>

12

찾기 / 이름 바꾸기 솔루션입니다. rename 은 util-linux의 일부입니다.

공백 파일 이름이 공백 디렉토리의 일부일 수 있으므로 먼저 깊이를 내림차순으로 내려야합니다.

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"

나는 당신을 실행할 때 전혀 변화가 없습니다.
추후 공지가있을 때까지 일시 중지되었습니다.

util-linux 설정을 확인하십시오 : $ rename --version rename (util-linux-ng 2.17.2)
Jürgen Hötzel

Greus / usr / bin / rename (Perl 스크립트)을 보면 "Revision 1.5 1998/12/18 16:16:31 rmb1"이 나타납니다.
추후 공지가있을 때까지 일시 중지되었습니다.

흠 ... 당신의 유틸리티 리눅스 바이너리는 어디로 갔습니까? 이 파일 경로는 util-linux가 소유해야합니다. 우리는 GNU-Linux 시스템이 아니십니까?
Jürgen Hötzel

1
내 실행에서 공간 만 변경 하므로 "산에 불을 지르십시오"가 "산에 불을 지으십시오"가됩니다.
brokkr

6

배쉬 4.0

#!/bin/bash
shopt -s globstar
for file in **/*\ *
do 
    mv "$file" "${file// /_}"       
done

파일 또는 디렉토리 이름에 공백이없는 경우 mv가 자체적으로 수행됩니다 (mv : a' to a subdirectory of itself, a / a를 이동할 수 없음 )
armandino

중요하지 않습니다. 로 리디렉션하여 오류 메시지를 제거하십시오 /dev/null.
ghostdog74

ghostdog, mv5 개의 파일을 5 만 번 생성 하여 4 개의 파일 이름 만 바꾸면 메시지가 넘치지 않더라도 약간의 오버 헤드가 발생할 수 있습니다.
Michael Krelin-해커

krelin, find는 공백이있는 파일을 찾은 다음 이름을 변경하기 위해 언급 한 55000 파일을 통과합니다. 백엔드에서는 여전히 모든 과정을 거칩니다. 원하는 경우 이름을 바꾸기 전에 공백을 처음 확인하면됩니다.
ghostdog74

나는 mv를 스폰하는 것에 대해 이야기하고 있었지만 통과하지 않았습니다. 그렇지 for file in *' '*않거나 일부는 더 나은 일을하지 않습니까?
Michael Krelin-해커

6

이것을 사용할 수 있습니다 :

    find . -name '* *' | while read fname 

do
        new_fname=`echo $fname | tr " " "_"`

        if [ -e $new_fname ]
        then
                echo "File $new_fname already exists. Not replacing $fname"
        else
                echo "Creating new file $new_fname to replace $fname"
                mv "$fname" $new_fname
        fi
done

3

Naidim의 답변 재귀 버전.

find . -name "* *" | awk '{ print length, $0 }' | sort -nr -s | cut -d" " -f2- | while read f; do base=$(basename "$f"); newbase="${base// /_}"; mv "$(dirname "$f")/$(basename "$f")" "$(dirname "$f")/$newbase"; done

2

다음은 "파일이 이미 존재합니다"경고를 stderr에 쓰는 (quit verbose) find -exec 솔루션입니다.

function trspace() {
   declare dir name bname dname newname replace_char
   [ $# -lt 1 -o $# -gt 2 ] && { echo "usage: trspace dir char"; return 1; }
   dir="${1}"
   replace_char="${2:-_}"
   find "${dir}" -xdev -depth -name $'*[ \t\r\n\v\f]*' -exec bash -c '
      for ((i=1; i<=$#; i++)); do
         name="${@:i:1}"
         dname="${name%/*}"
         bname="${name##*/}"
         newname="${dname}/${bname//[[:space:]]/${0}}"
         if [[ -e "${newname}" ]]; then
            echo "Warning: file already exists: ${newname}" 1>&2
         else
            mv "${name}" "${newname}"
         fi
      done
  ' "${replace_char}" '{}' +
}

trspace rootdir _

2

이것은 조금 더합니다. 다운로드 한 급류의 이름을 바꾸는 데 사용합니다 (특수 문자 (비 ASCII), 공백, 여러 점 등 없음).

#!/usr/bin/perl

&rena(`find . -type d`);
&rena(`find . -type f`);

sub rena
{
    ($elems)=@_;
    @t=split /\n/,$elems;

    for $e (@t)
    {
    $_=$e;
    # remove ./ of find
    s/^\.\///;
    # non ascii transliterate
    tr [\200-\377][_];
    tr [\000-\40][_];
    # special characters we do not want in paths
    s/[ \-\,\;\?\+\'\"\!\[\]\(\)\@\#]/_/g;
    # multiple dots except for extension
    while (/\..*\./)
    {
        s/\./_/;
    }
    # only one _ consecutive
    s/_+/_/g;
    next if ($_ eq $e ) or ("./$_" eq $e);
    print "$e -> $_\n";
    rename ($e,$_);
    }
}

1

나는이 스크립트 주위에서 발견했다, 그것은 재미 있을지도 모른다 :)

 IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS

이름에 줄 바꿈이있는 파일에서 실패합니다.
ghoti


1

macOS를 사용하여이 문제를 해결하려면 먼저 모든 도구를 설치하십시오.

 brew install tree findutils rename

그런 다음 이름을 바꿀 필요가있을 때 GNU find (gfind)의 별칭을 find로 만드십시오. 그런 다음 @Michel Krelin의 코드를 실행하십시오.

alias find=gfind 
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done   

0

이것은 현재 디렉토리 내 에서만 파일을 찾고 이름을 바꿉니다 . 이 별칭이 있습니다.

find ./ -name "* *" -type f -d 1 | perl -ple '$file = $_; $file =~ s/\s+/_/g; rename($_, $file);


0

나는 단지 내 목적을 위해 하나를 만듭니다. 참조로 사용할 수 있습니다.

#!/bin/bash
cd /vzwhome/c0cheh1/dev_source/UB_14_8
for file in *
do
    echo $file
    cd "/vzwhome/c0cheh1/dev_source/UB_14_8/$file/Configuration/$file"
    echo "==> `pwd`"
    for subfile in *\ *; do [ -d "$subfile" ] && ( mv "$subfile" "$(echo $subfile | sed -e 's/ /_/g')" ); done
    ls
    cd /vzwhome/c0cheh1/dev_source/UB_14_8
done

0

/ files라는 폴더에있는 파일의 경우

for i in `IFS="";find /files -name *\ *`
do
   echo $i
done > /tmp/list


while read line
do
   mv "$line" `echo $line | sed 's/ /_/g'`
done < /tmp/list

rm /tmp/list

0

문제에 대한 나의 해결책은 bash 스크립트입니다.

#!/bin/bash
directory=$1
cd "$directory"
while [ "$(find ./ -regex '.* .*' | wc -l)" -gt 0 ];
do filename="$(find ./ -regex '.* .*' | head -n 1)"
mv "$filename" "$(echo "$filename" | sed 's|'" "'|_|g')"
done

스크립트를 실행 한 후 스크립트를 적용 할 디렉토리 이름을 인수로 입력하십시오.


0

macOS에서

선택한 답변과 같습니다.

brew install rename

# 
cd <your dir>
find . -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find . -name "* *" -type f | rename 's/ /_/g'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.