디렉토리를 병합하지만 새 파일 이름에서 디렉토리 이름을 유지


11

다음 형식으로 디렉토리를 평평하게하려면 어떻게해야합니까?

전에: ./aaa/bbb/ccc.png

후: ./aaa-bbb-ccc.png


PS : 홀수 문자 (예 : 공백)가있는 파일 이름도 고려해야합니다.
Nathan JB

3
당신이 가능한 경우 위치를 처리하는 방법을 또한 지정해야 ./foo/bar/baz.png하고 ./foo-bar-baz.png모두 존재합니다. 나는 당신이 후자를 전자로 바꾸고 싶지 않다고 가정합니까?
jw013

내 경우에는 관련이 없지만 다른 사람들이 그것에 의존 할 수 있도록 그 대답이 실제로 설명되어야합니다! 감사!
Nathan JB

답변:


7

경고 : 대부분의 명령을 브라우저에 직접 입력했습니다. 주의 사항 강사.

zsh 및 zmv 사용 :

zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'

설명 : 패턴 **/*은 현재 디렉토리의 서브 디렉토리에있는 모든 파일을 재귀 적으로 일치시킵니다 (현재 디렉토리의 파일과 일치하지는 않지만 이름을 바꿀 필요는 없습니다). 괄호의 처음 두 쌍에 대해 참조로 할 수있는 기 $1$2대체 텍스트. 마지막 괄호 쌍은 D glob 한정자를 추가하여 도트 파일이 생략되지 않도록합니다. 기존 파일을 덮어 쓸 것인지 묻는 메시지가 표시되도록 옵션 -o -i을 전달하는 것을 의미합니다 .-imv


POSIX 도구 만 사용 :

find . -depth -exec sh -c '
    for source; do
      case $source in ./*/*)
        target="$(printf %sz "${source#./}" | tr / -)";
        mv -i -- "$source" "${target%z}";;
      esac
    done
' _ {} +

설명 : case명령문이 현재 디렉토리 및 현재 디렉토리의 최상위 서브 디렉토리를 생략합니다. target소스 파일 이름 ( $0)을 포함하고 선행 줄을 ./제거하고 모든 슬래시를 대시로 바꾸고 final을 포함 z합니다. 마지막 z은 파일 이름이 줄 바꿈으로 끝나는 경우입니다. 그렇지 않으면 명령 대체가 파일을 제거합니다.

find지원하지 않는 경우 -exec … +(OpenBSD,보고 있습니다) :

find . -depth -exec sh -c '
    case $0 in ./*/*)
      target="$(printf %sz "${0#./}" | tr / -)";
      mv -i -- "$0" "${target%z}";;
    esac
' {} \;

bash (또는 ksh93)를 사용하면 슬래시를 대시로 대체하기 위해 외부 명령을 호출 할 필요가 없으며 ksh93 매개 변수 확장을 문자열 대체 구문과 함께 사용할 수 있습니다 ${VAR//STRING/REPLACEMENT}.

find . -depth -exec bash -c '
    for source; do
      case $source in ./*/*)
        source=${source#./}
        target="${source//\//-}";
        mv -i -- "$source" "$target";;
      esac
    done
' _ {} +

for source예는 내가 마지막으로 당신은 (일반적으로) 사용한 이유를 발견했습니다 ... 처음 소개 파일 / 디렉토리를 그리워 _$0:) ... 그리고 덕분에 좋은 답변을. 나는 그들로부터 많은 것을 배웁니다.
Peter.O

zmv 예제는 드라이 런만 수행합니다. 이동 명령을 인쇄하지만 실행하지는 않습니다. 실제로 이러한 명령을 실행하려면 -Qn에서 n을 제거하십시오.
Peter

5

여기에 좋은 대답이 있지만 다음과 같이 더 직관적입니다 bash.

find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;

aaa기존 디렉토리는 어디에 있습니까 ? 포함 된 파일은 현재 디렉토리로 이동합니다 (aaa에는 빈 디렉토리 만 남음). 두 호출tr디렉토리와 공백을 모두 처리 위한 두 (이스케이프 된 슬래시에 대한 논리없이 독자를위한 연습).

나는 이것을보다 직관적이고 조정하기 쉽다고 생각합니다. 즉, find.png 파일 만 찾으려고 하면 매개 변수를 변경할 수 있습니다 . tr통화 를 변경 하거나 필요에 따라 추가 할 수 있습니다 . 그리고 그것이 옳은 일을 할 것인지 육안으로 확인 echo하기 전에 추가 할 수 있습니다 mv(그러면 echo상황이 올바르게 보일 때만 추가없이 반복하십시오 :

find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;

또한 후자는 두 개가 최종 파일 이름에 재미있는 유물을 남길 수 있기 find aaa때문에 find .... 또는 find aaa/...을 사용 하지 않습니다 .


이 라이너 하나에 감사드립니다-디렉토리 외부에서했을 때 잘 작동했습니다 (정확하게 말한 것입니다). 루트 디렉토리 이름이 추가되는 것을 피하기 위해 디렉토리 내부에서 시도했을 때 모든 파일이 사라졌습니다. 나는 그것들을 같은 디렉토리에 숨겨진 파일로 만들었습니다.
dgig

2
find . -mindepth 2 -type f -name '*' |
  perl -l000ne 'print $_;  s/\//-/g; s/^\.-/.\// and print' |
    xargs -0n2 mv 

참고 :이 포함 ​​된 파일 이름에는 작동하지 않습니다 \n.
이것은 물론 형식 f파일 만 이동 합니다.
유일한 이름 충돌은 기존 파일에 존재합니다. pwd에

이 기본 하위 집합으로 테스트

rm -fr junk
rm -f  junk*hello*

mkdir -p  junk/junkier/junkiest
touch    'hello    hello'
touch    'junk/hello    hello'
touch    'junk/junkier/hello    hello'
touch    'junk/junkier/junkiest/hello    hello'

를 야기하는

./hello    hello
./junk-hello    hello
./junk-junkier-hello    hello
./junk-junkier-junkiest-hello    hello

0

여기에 ls버전이 있습니다. 의견 환영합니다. 이 같은 엉뚱한 파일 이름에는 작동 "j1ळ\n\001\n/j2/j3/j4/\nh  h\n"하지만 실제로 함정에 대해 알고 싶습니다. "악성 인들이 ls실제로 (견고하게) 그것을 할 수 있는가?"

ls -AbQR1p * |                        # paths in "quoted" \escaped format
    sed -n '/":$/,${/.[^/]$/p}' |     # skip files in pwd, blank and dir/ lines
    { bwd="$PWD"; while read -n1;     # save base dir strip leading "quote
                        read -r p; do # read path
        printf -vs "${p%\"*}"         # strip trailing quote"(:)
        [[ $p == *: ]] && {           # this is a directory
            cd   "$bwd/$s"            # change into new directory
            rnp="${s//\//-}"-         # relative name prefix
        } || mv "$s" "$bwd/$rnp$s"
      done; }

-1

파일 이름 + 경로를 tr명령으로 파이프하십시오 .tr <replace> <with>

for FILE in `find aaa -name "*.png"`
do
  NEWFILE=`echo $FILE | tr '/' '-'`
  cp -v $FILE $NEWFILE
done

1
이상한 파일 이름을 안전하게 처리하지 못합니다. 공간은 (다른 것들 중에서) 그것을 깨뜨릴 것입니다.
jw013

언뜻보기에 이것은 깨끗하고 읽기 쉬운 솔루션이지만 @ jw013은 맞습니다. 공간을 잘 처리하지 못합니다. 도움 cpcp -v "$FILE" "$NEWFILE"주기 위해 줄을 바꾸겠습니까 ? (또한 PNG 파일 만 가정해서는 안됩니다.)
Nathan JB

2
@ NathanJ.Brauer cp줄에 따옴표를 추가하는 것만으로 는 충분하지 않습니다. 루프를 find사용하여 출력 을 구문 분석하는 전체 접근 방식에 for결함이 있습니다. 사용의 유일한 안전한 방법이 find함께있는 -exec, 또는 -print0(이하 휴대용 가능한 경우 -exec). 더 강력한 대안을 제안하지만 현재 귀하의 질문이 잘못 지정되었습니다.
jw013

-2

파일 이름의 공백에 안전 :

#!/bin/bash

/bin/find $PWD -type f | while read FILE
do
    _new="${FILE//\//-}"
    _new="${_new:1}"
    #echo $FILE
    #echo $_new

    /bin/mv "$FILE" "$_new"
done

명백한 이유 cp대신에 채굴을 mv했지만 같은 결과를 낳아야합니다.


3
type findfind is /usr/bin/find내 컴퓨터에서 -> . 사용 PATH하는 스크립트의 변수 이름으로하는 것은 끔찍한 생각이다. 또한 스크립트는 파일 이름을 공백이나 백 슬래시로 엉망으로 만듭니다. read명령 을 잘못 사용하기 때문입니다 (이 사이트에서 검색 IFS read -r).
Gilles 'SO- 악한 중지'

find is hashed (/bin/find)관련 방법? 다른 배포판이 다른가요?
Tim

독수리 미끼에서 멀리 떨어져 있었으면 좋겠다.
Tim

@Tim 당신은 항상 답변을 삭제하여 담당자를 돌려받을 수 있습니다 (그리고 공제 비용은 공제 비용이 청구됩니다). 스크립트로의 하드 코딩 경로 PATH모든 유닉스 시스템이 사용 하는 환경 변수 의 요점이 빠져 있다고 제안하는 것 같습니다 . 작성하면 /bin/find스크립트가없는 모든 시스템 (Gilles, mine 및 아마도 OP)에서 실제로 스크립트가 손상됩니다 /bin/find(예 : b / c가 있습니다 /usr/bin/find). PATH환경 변수의 요점은 이것을 비 문제로 만드는 것입니다.
jw013

그건 그렇고, $(pwd)이미 변수에서 사용할 수 있습니다 : $PWD. 그러나 여기서 절대 경로를 사용 해서는 안됩니다 . 예를 들어 스크립트를 호출 /home/tim하면로 이름 /home/tim/some/path을 바꾸려고 시도 합니다 /home-tim-some-path.
Gilles 'SO- 악마 그만'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.