Linux : 대상 디렉토리가 존재하지 않으면 복사 및 작성


348

대상 디렉토리가 존재하지 않는 경우 대상 디렉토리를 작성하는 명령 (또는 아마도 cp 옵션)을 원합니다.

예:

cp -? file /path/to/copy/file/to/is/very/deep/there


5
허용되는 답변이 잘못되었습니다 (cp에는 이러한 옵션이 있습니다). Paul Whipp의 두 번째 답변을 참조하십시오.
Ronenz

1
@Ronenz, 나는 GNU에 언급할만한 옵션이 있다고 동의 cp하지만 (Paul Whipp의 답변에 따라) OP가 요청한 것만 큼 일반적이지 않습니다. 그것은 복사 할 수 있습니다 a/foo/bar/fileb/foo/bar/file있지만 복사 할 수 없습니다 fileb/foo/bar/file. (즉, 첫 번째 파일의 경우 이미 존재하는 상위 디렉토리 만 작성하지만 임의의 디렉토리를 작성할 수는 없습니다.)
Sudo Bash

그것은 cp에 대한 멋진 기능 요청처럼 보입니다. 가까운 시일 내에 그러한 명령 행 스위치를 얻을 수 있을까요?
Arnaud

답변:


328
mkdir -p "$d" && cp file "$d"

(에 대한 해당 옵션이 없습니다 cp).


62
나는 당신이 필요하다고 생각하지 않습니다 test -d: mkdir -p디렉토리가 이미 존재하더라도 여전히 성공합니다.
ephemient

죄송합니다. 그러나 테스트는 bash 내장 (특히 [[-d "$ d"]]로 작성된 경우) 일 수 있으며 mkdir은 ;-) 할 수 없습니다.
Michael Krelin-Hacker

1
mkdirBusyBox에 내장되어 있습니다;)
ephemient

7
@holms $d는 아마도 해당 디렉토리를 포함하는 변수입니다. 내 말은, 세 번 반복해야한다면 변수에 먼저 할당 할 가능성이 큽니다.
Michael Krelin-해커

237

다음 둘 다에 해당되는 경우 :

  1. GNU 버전 cp(예 : Mac 버전은 아님)을 사용하고 있으며
  2. 기존 디렉토리 구조에서 복사 중이며 다시 작성하면됩니다.

그런 다음의 --parents플래그를 사용 하여이 작업을 수행 할 수 있습니다 cp. [정보] 페이지에서 (볼에서 http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation 또는 info cpman cp) :

--parents
     Form the name of each destination file by appending to the target
     directory a slash and the specified name of the source file.  The
     last argument given to `cp' must be the name of an existing
     directory.  For example, the command:

          cp --parents a/b/c existing_dir

     copies the file `a/b/c' to `existing_dir/a/b/c', creating any
     missing intermediate directories.

예:

/tmp $ mkdir foo
/tmp $ mkdir foo/foo
/tmp $ touch foo/foo/foo.txt
/tmp $ mkdir bar
/tmp $ cp --parents foo/foo/foo.txt bar
/tmp $ ls bar/foo/foo
foo.txt

4
Mac OS X에서는 작동하지 않으므로 Linux와 관련이 있다고 생각합니다.
olt

7
gnu 특정이라고 말하고 싶습니다. 가지고 있다면 macportscoreutils와 그 유틸리티를 설치할 수 있습니다 gcp.
Michael Krelin-해커

16
사람, 리눅스는 모든 것을
Ziggy

19
나는 이것이 내가 찾던 대답 (그러나 내가 찬성 한 대답)이지만 약간 다른 질문에 대한 답이라고 생각합니다. 목적지 부모 디렉토리를 원래 부모 디렉토리와 동일하게하고 싶다고 가정하면 이것이 해결책이라고 생각합니다. 아마도 이것을 읽는 대부분의 사람들이 흥미있는 유스 케이스 일 것입니다.
David Winiecki

7
이것은 'bar'디렉토리가 이미 있다고 가정하지만 원래 질문은 대상으로 제공되고 아직 존재하지 않을 때 'bar'를 자동으로 작성하는 방법을 암시합니다. 이에 대한 답변은 @ MichaelKrelin-hacker가 제공합니다.
thdoan

69

짧은 답변

에 복사하려면 다음 myfile.txt/foo/bar/myfile.txt사용하십시오.

mkdir -p /foo/bar && cp myfile.txt $_

어떻게 작동합니까?

여기에는 몇 가지 구성 요소가 있으므로 모든 구문을 단계별로 다룰 것입니다.

에서 mkdir 유틸리티는 POSIX 표준에 지정된대로 , 디렉토리를 만든다. -p인수는 워드 프로세서 당 원인이됩니다 MKDIR

누락 된 중간 경로 이름 구성 요소 작성

호출 할 때 즉 mkdir -p /foo/bar, MKDIR이 만들어집니다 /foo /foo/bar 경우 /foo않습니다 존재하지. (없이는 -p대신 오류가 발생합니다.

&&목록 연산자는에 설명 된대로 POSIX 표준 (또는 배쉬 설명서 원하는 경우), 효과가 cp myfile.txt $_있는 경우에만 실행됩니다 mkdir -p /foo/bar성공적으로 실행합니다. 이는 여러 가지 이유로 실패한 cp경우 명령이 실행되지 않음을 의미합니다 .mkdir

마지막으로, $_두 번째 인수로 전달 cp하는 "특수 매개 변수"는 변수에 저장하지 않고도 긴 경로 (파일 경로와 같은)를 반복하지 않도록하는 데 유용합니다. 당 배쉬 설명서 , 그것은 :

이전 명령의 마지막 인수로 확장

이 경우에 /foo/bar우리가 전달한 것 mkdir입니다. 따라서 cp명령이로 확장되어 새로 작성된 디렉토리에 cp myfile.txt /foo/bar복사 myfile.txt됩니다 /foo/bar.

참고 $_입니다 하지 POSIX 표준의 일부는 , 그래서 이론적으로 유닉스 변종이 구조를 지원하지 않는 쉘이있을 수 있습니다. 그러나, 나는 지원하지 않는 현대 쉘을 모른다 $_. Bash, Dash 및 zsh는 모두 가능합니다.


마지막 참고 사항 :이 대답의 시작 부분에 입력 한 명령은 디렉토리 이름에 공백이 없다고 가정합니다. 공백이있는 이름을 처리하는 경우 다른 단어를 인용하도록 인용해야합니다 mkdir또는에 대한 다른 인수로 취급되지 않습니다 cp. 따라서 명령은 실제로 다음과 같습니다.

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"

16
따로 : 나는 일반적으로 스택 오버 플로우의 Bash 또는 Unix 쉘 명령에 대한 질문에 대한 세부 정보가 부족하여 실망합니다. 이는 Unix 마법사가 아닌 경우 선택하기 어려운 복잡하고 매우 조밀 한 1 라이너를 자주 버립니다. 나는 반대의 극단적 인 것을 시도했고 구문의 모든 세부 사항을 설명 하고이 장소의 작동 방식에 대한 문서를 찾기 위해 적절한 장소에 링크했습니다. 나는 이것이 적어도 일부 사람들에게 도움이되기를 바랍니다. 또한, 인해 신용 : 나는 중복 질문이 대답에서이 방법을 다친 : stackoverflow.com/a/14085147/1709587
마크 Amery

나는 전에 $ _를 본 적이 없다. 그것은 무엇을 의미합니까? 마지막 명령에 사용 된 폴더?
thebunnyrules

9
@thebunnyrules 그러나 ...하지만 "어떻게 작동합니까?" 문자 그대로 방금 질문 한 질문에 전념하는 여러 단락이 포함되어 있습니다. 무시되고 사랑받지 못하는 느낌입니다. :(
Mark Amery

미안합니다. 당신 말이 맞아요 나는 이미 당신의 대답에 다른 모든 것을 알고 있었기 때문에 빨리 스캔했습니다. 좀 더 자세히 읽어 보았어야 했어요.
thebunnyrules

와우, 당신은 정말 많은 답변을 그 답에 넣었습니다 ... 고마워요. $ _에 대해 배우게되어 기뻤습니다. 나는 지금부터 그것을 꽤 많이 사용하고 있음을 알 수 있습니다. 전체 프로세스를 자동화하고이 스레드를 작성하는 스크립트를 작성했습니다. 당신이 좋아할 것입니다 : stackoverflow.com/questions/1529946/…
thebunnyrules

39

그런 오래된 질문이지만 대체 솔루션을 제안 할 수 있습니다.

install프로그램을 사용 하여 파일을 복사하고 "즉시"대상 경로를 만들 수 있습니다.

install -D file /path/to/copy/file/to/is/very/deep/there/file

그러나 고려해야 할 몇 가지 측면이 있습니다.

  1. 대상 경로뿐만 아니라 대상 파일 이름도 지정 해야 합니다.
  2. 대상 파일 은 실행 가능합니다 (적어도 테스트에서 본 한)

-m대상 파일에 대한 권한을 설정 하는 옵션을 추가하여 # 2를 쉽게 수정할 수 있습니다 (예 : -m 664을 사용하여 rw-rw-r--새 파일을 만드는 것처럼 권한이있는 대상 파일을 만듭니다 touch).


그리고 여기에 내가 영감을 얻은 대답에 대한 부끄러운 연결이 있습니다 =)


실제로 내 사용법에는 효과가 없었지만 멋진 트릭입니다! 명심하겠습니다.
thebunnyrules

이 명령은 755( rwxr-xr-x) 권한을 가진 대상 파일을 작성하는데 , 이는 바람직하지 않습니다. -m스위치로 다른 것을 지정할 수는 있지만 파일 권한을 유지 하는 방법을 찾을 수 없었습니다. :(
törzsmókus

19

파일을 살 수있는 구멍을 파기 때문에 원하는 것을하는 "버리"사본이라고하는 쉘 함수 :

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }

1
당신은 dirname $2또한 주위에 인용 부호가 있어야합니다
Tarnay Kálmán

4
@Kalmi, 적절한 인용을 위해에 $2대한 인수로 인용하고 싶습니다 dirname. 처럼 mkdir -p "$(dirname "$2")". 이 인용하지 않고 $2에서하는 것은 cp쓸모가)
마이클 Krelin - 해커

3
이 답변은 $ 2가 아직 대상 디렉토리가 아니라고 가정합니다. 그렇지 않으면 함수 본문으로 "mkdir -p"$ 2 "&& cp"$ 1 ""$ 2 "를
원합니다.

@ user467257이 지적했듯이 버그가 있다고 생각합니다. 이 시나리오에서 대상 디렉토리와 대상 파일 사이에 $ 2가 모호하기 때문에 이것이 고칠 수 있다고 생각하지 않습니다. 이 문제를 해결하는 다른 답변을 게시했습니다.
Rich

@ Rich, 나는 이것이 고칠 수 없다는 것에 동의하지 않는다. 다음은 파일과 디렉토리 모두에서 잘 작동합니다. mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; 주변의 백틱 dirname $2은 코드 마커로 구문 분석하기 때문에 표시되지 않습니다.
Yury

11

이를 수행하는 한 가지 방법이 있습니다.

mkdir -p `dirname /path/to/copy/file/to/is/very/deep/there` \
   && cp -r file /path/to/copy/file/to/is/very/deep/there

dirname대상 디렉토리 또는 파일의 부모를 제공합니다. mkdir -p`dirname ...`은 cp -r을 호출 할 때 올바른 기본 디렉토리가 있는지 확인하는 해당 디렉토리를 작성합니다.

--parents에 비해이 방법의 장점은 대상 경로의 마지막 요소가 파일 이름 인 경우 작동한다는 것입니다.

그리고 그것은 OS X에서 작동합니다.


6

install -D file -m 644 -t /path/to/copy/file/to/is/very/deep/there


1
실제로는 전혀 다릅니다. 그의 파일 이름을 두 번 전달해야하므로 ( '-t'플래그) 파일에서 실행 비트를 설정합니다 (따라서 '-m'예제). 그의 대답은 실제로 질문에 대답하지 않기 때문에 그의 대답은 최고가 아니 어야합니다 .
Spongman

1
단일 파일에서 작동합니다. there최종 서브 디렉토리가 아닌 최종 파일 인 파일 만 고려 하십시오.
kvantour

6

위의 답변을 모두 존중하면서 다음과 같이 rsync를 사용하는 것을 선호합니다.

$  rsync -a directory_name /path_where_to_inject_your_directory/

예:

$ rsync -a test /usr/local/lib/

나는 그것을 시도 하고이 결과를 간다 : rsync -a 황소 / some / hoop / ti / doop / ploop 결과 rsync : mkdir "/ some / hoop / ti / doop / ploop"실패 : 해당 파일이나 디렉토리가 없습니다 (2) rsync 오류 : main.c (675)의 파일 IO (코드 11) 오류 [수신자 = 3.1.2]
thebunnyrules

@thebunny는 pwd 명령으로 경로를 확인하고 (: [link] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) 명령에 경로를 올바르게 입력해야합니다
marcdahan

제안 사항을 올바르게 이해하면 소스의 전체 경로를 지정하는 것이 좋습니다. 파일 이름 대신 bull : "? 문제는 소스 (불)가 아닌 대상 디렉토리를 만드는 rsync에 오류가 있다는 것입니다. rsync는 mkdir -p가 아닌 간단한 mkdir을 사용합니다.
thebunnyrules

1
그건 그렇고 훌륭한 유틸리티, 스크립트에서 사용하기 시작할 수 있습니다. 추천 해 주셔서 감사합니다.
thebunnyrules

1
rsync 보다 예측 가능합니다 cp. 내가하고자하는 경우 예를 들어, cp /from/somedir /to-dir다음의 CP 동작합니다 다른 경우는 /to-dir이미 존재 : 만약이 /to-dir다음, 존재 cp만들 것이다 /to-dir/some-dir, 그렇지 않으면 단지 내용 /from/somedir에 배치됩니다 /to-dir. 그러나, rsync즉, 경우에 동일하게 동작 /to-dir/some-dir합니다 항상 생성.
JoeAC

5

한 줄로 재개하고 완벽한 작업 솔루션을 제공하십시오. 파일 이름을 바꾸려면 조심하십시오. mkdir에 깨끗한 dir 경로를 제공하는 방법을 포함시켜야합니다. $ fdst는 file 또는 dir 일 수 있습니다. 다음 코드는 어떤 경우에도 작동해야합니다.

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p $(dirname ${fdst}) && cp -p ${fsrc} ${fdst}

또는 배쉬 특정

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}

감사! 이것이 내가 필요한 것입니다. 대상 디렉토리를 가지고 여부 및 디렉토리가 존재하는지 여부를이 코드는 나에게 존재하지 않는 경우 다음 내가 안전하게 만들 수있는 상위 디렉토리 제공, 그래서 내 경우에는 내가 모르는
xbmono

4

.bashrc에 다음을 추가하고 필요한 경우 조정하십시오. 우분투에서 작동합니다.

mkcp() {
    test -d "$2" || mkdir -p "$2"
    cp -r "$1" "$2"
}

예 : 'test'파일을 대상 디렉토리 'd'에 복사하려면 사용,

mkcp test a/b/c/d

mkcp 는 먼저 대상 디렉토리가 존재하는지 여부를 확인한 후 존재하지 않으면 소스 파일 / 디렉토리를 복사합니다.


다른 사람들이 다른 답변에 댓글을 달았으므로을 호출하기 전에 디렉토리가 존재하는지 테스트 할 필요가 없습니다 mkdir -p. 이미 존재하더라도 성공합니다.
bfontaine

3

이것은 나를 위해 그것을한다

cp -vaR ./from ./to

동의하다. 에서 man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
borracciaBlu

3

나는 정확히 이것을하기위한 CP (참고 대문자)라는 cp에 대한 지원 스크립트를 작성했습니다. 스크립트는 경로에 넣은 경로에서 오류를 확인하고 (대상인 마지막 경로는 제외) 모든 것이 정상이면 복사를 시작하기 전에 대상 경로를 만들기 위해 mkdir -p 단계를 수행합니다. 이 시점에서 일반 cp 유틸리티가 대신 사용하고 CP와 함께 사용하는 모든 스위치 (예 : -r, -p, -rpL은 cp로 직접 파이프 됨) 내 스크립트를 사용하기 전에 몇 가지 이해해야 할 것이 있습니다.

  • 여기에있는 모든 정보는 CP --help를 수행하여 액세스 할 수 있습니다. CP --help-all include의 cp 스위치.
  • 일반 cp는 대상 경로를 찾지 못하면 복사를 수행하지 않습니다. CP를 사용한 오타 용 안전망이 없습니다. 목적지가 만들어 지므로 / usrr / share / icons 또는 / usr / share / icon로 목적지의 철자를 잘못 입력하면 바로 이것이 만들어 질 것입니다.
  • 일반 cp는 기존 경로에서 동작을 모델링하는 경향이 있습니다. cp / a / b / c / d는 d의 존재 여부에 따라 다릅니다. d가 기존 폴더 인 경우 cp는 b를 복사하여 / c / d / b를 만듭니다. d가 존재하지 않으면 b는 c로 복사되고 d로 이름이 바뀝니다. d가 존재하지만 파일이고 b가 파일 인 경우 b의 사본으로 덮어 씁니다. c가 존재하지 않으면 cp는 복사를 수행하지 않고 종료합니다.

CP는 기존 경로에서 신호를 얻는 데 사치가 없으므로 매우 확실한 행동 패턴을 가져야합니다. CP는 복사중인 항목이 대상 경로에서 삭제되고 대상 자체가 아니라고 가정합니다 (일명 소스 파일 / 폴더의 이름이 바뀐 사본). 의미:

  • d가 폴더 인 경우 "CP / a / b / c / d"는 / c / d / b가됩니다.
  • / c / b의 b가 폴더 인 경우 "CP / a / b / c / b"는 / c / b / b가됩니다.
  • b와 d가 모두 파일 인 경우 : CP / a / b / c / d는 / c / d가됩니다 (여기서 d는 b의 사본 임). 동일한 상황에서 CP / a / b / c / b와 동일합니다.

이 기본 CP 동작은 "--rename"스위치를 사용하여 변경할 수 있습니다. 이 경우에는

  • "CP --rename / a / b / c / d"는 b를 / c에 복사하고 사본의 이름을 d로 바꾸는 중입니다.

몇 가지 참고 사항 : cp와 마찬가지로 CP는 마지막 경로가 대상으로 가정하여 한 번에 여러 항목을 복사 할 수 있습니다. 따옴표를 사용하는 한 공백이있는 경로를 처리 할 수도 있습니다.

CP는 복사 한 경로를 확인하고 복사하기 전에 경로가 존재하는지 확인합니다. 엄격 모드 (--strict 스위치를 통해 사용 가능)에서는 복사중인 모든 파일 / 폴더가 존재해야합니다. 완화 모드 (-휴식)에서 나열된 항목 중 하나 이상이 존재하면 복사가 계속됩니다. 완화 모드가 기본값이며 스위치를 통해 모드를 일시적으로 변경하거나 스크립트 시작 부분에 easy_going 변수를 설정하여 영구적으로 모드를 변경할 수 있습니다.

설치 방법은 다음과 같습니다.

루트가 아닌 터미널에서 다음을 수행하십시오.

sudo echo > /usr/bin/CP; sudo chmod +x /usr/bin/CP; sudo touch /usr/bin/CP
gedit admin:///usr/bin/CP 

gedit에서 CP 유틸리티를 붙여넣고 저장하십시오.

#!/bin/bash
#Regular cp works with the assumption that the destination path exists and if it doesn't, it will verify that it's parent directory does.

#eg: cp /a/b /c/d will give /c/d/b if folder path /c/d already exists but will give /c/d (where d is renamed copy of b) if /c/d doesn't exists but /c does.

#CP works differently, provided that d in /c/d isn't an existing file, it assumes that you're copying item into a folder path called /c/d and will create it if it doesn't exist. so CP /a/b /c/d will always give /c/d/b unless d is an existing file. If you put the --rename switch, it will assume that you're copying into /c and renaming the singl item you're copying from b to d at the destination. Again, if /c doesn't exist, it will be created. So CP --rename /a/b /c/d will give a /c/d and if there already a folder called /c/d, contents of b will be merged into d. 

#cp+ $source $destination
#mkdir -p /foo/bar && cp myfile "$_"

err=0 # error count
i=0 #item counter, doesn't include destination (starts at 1, ex. item1, item2 etc)
m=0 #cp switch counter (starts at 1, switch 1, switch2, etc)
n=1 # argument counter (aka the arguments inputed into script, those include both switches and items, aka: $1 $2 $3 $4 $5)
count_s=0
count_i=0
easy_going=true #determines how you deal with bad pathes in your copy, true will allow copy to continue provided one of the items being copied exists, false will exit script for one bad path. this setting can also be changed via the custom switches: --strict and --not-strict
verbal="-v"


  help="===============================================================================\
    \n         CREATIVE COPY SCRIPT (CP) -- written by thebunnyrules\
    \n===============================================================================\n
    \n This script (CP, note capital letters) is intended to supplement \
    \n your system's regular cp command (note uncapped letters). \n
    \n Script's function is to check if the destination path exists \
    \n before starting the copy. If it doesn't it will be created.\n    
    \n To make this happen, CP assumes that the item you're copying is \
    \n being dropped in the destination path and is not the destination\
    \n itself (aka, a renamed copy of the source file/folder). Meaning:\n 
    \n * \"CP /a/b /c/d\" will result in /c/d/b \
    \n * even if you write \"CP /a/b /c/b\", CP will create the path /a/b, \
    \n   resulting in /c/b/b. \n
    \n Of course, if /c/b or /c/d are existing files and /a/b is also a\
    \n file, the existing destination file will simply be overwritten. \
    \n This behavior can be changed with the \"--rename\" switch. In this\
    \n case, it's assumed that \"CP --rename /a/b /c/d\" is copying b into /c  \
    \n and renaming the copy to d.\n
    \n===============================================================================\
    \n        CP specific help: Switches and their Usages \
    \n===============================================================================\n
    \
    \n  --rename\tSee above. Ignored if copying more than one item. \n
    \n  --quiet\tCP is verbose by default. This quiets it.\n
    \n  --strict\tIf one+ of your files was not found, CP exits if\
    \n\t\tyou use --rename switch with multiple items, CP \
    \n\t\texits.\n
    \n  --relaxed\tIgnores bad paths unless they're all bad but warns\
    \n\t\tyou about them. Ignores in-appropriate rename switch\
    \n\t\twithout exiting. This is default behavior. You can \
    \n\t\tmake strict the default behavior by editing the \
    \n\t\tCP script and setting: \n
    \n\t\teasy_going=false.\n
    \n  --help-all\tShows help specific to cp (in addition to CP)."

cp_hlp="\n\nRegular cp command's switches will still work when using CP.\
    \nHere is the help out of the original cp command... \
    \n\n===============================================================================\
    \n          cp specific help: \
    \n===============================================================================\n"

outro1="\n******************************************************************************\
    \n******************************************************************************\
    \n******************************************************************************\
    \n        USE THIS SCRIPT WITH CARE, TYPOS WILL GIVE YOU PROBLEMS...\
    \n******************************************************************************\
    \n******************************* HIT q TO EXIT ********************************\
    \n******************************************************************************"


#count and classify arguments that were inputed into script, output help message if needed
while true; do
    eval input="\$$n"
    in_=${input::1}

    if [ -z "$input" -a $n = 1 ]; then input="--help"; fi 

    if [ "$input" = "-h" -o "$input" = "--help" -o "$input" = "-?" -o "$input" = "--help-all" ]; then
        if [ "$input" = "--help-all" ]; then 
            echo -e "$help"$cp_hlp > /tmp/cp.hlp 
            cp --help >> /tmp/cp.hlp
            echo -e "$outro1" >> /tmp/cp.hlp
            cat /tmp/cp.hlp|less
            cat /tmp/cp.hlp
            rm /tmp/cp.hlp
        else
            echo -e "$help" "$outro1"|less
            echo -e "$help" "$outro1"
        fi
        exit
    fi

    if [ -z "$input" ]; then
        count_i=$(expr $count_i - 1 ) # remember, last item is destination and it's not included in cound
        break 
    elif [ "$in_" = "-" ]; then
        count_s=$(expr $count_s + 1 )
    else
        count_i=$(expr $count_i + 1 )
    fi
    n=$(expr $n + 1)
done

#error condition: no items to copy or no destination
    if [ $count_i -lt 0 ]; then 
            echo "Error: You haven't listed any items for copying. Exiting." # you didn't put any items for copying
    elif [ $count_i -lt 1 ]; then
            echo "Error: Copying usually involves a destination. Exiting." # you put one item and no destination
    fi

#reset the counter and grab content of arguments, aka: switches and item paths
n=1
while true; do
        eval input="\$$n" #input=$1,$2,$3,etc...
        in_=${input::1} #first letter of $input

        if [ "$in_" = "-" ]; then
            if [ "$input" = "--rename" ]; then 
                rename=true #my custom switches
            elif [ "$input" = "--strict" ]; then 
                easy_going=false #exit script if even one of the non-destinations item is not found
            elif [ "$input" = "--relaxed" ]; then 
                easy_going=true #continue script if at least one of the non-destination items is found
            elif [ "$input" = "--quiet" ]; then 
                verbal=""
            else
                #m=$(expr $m + 1);eval switch$m="$input" #input is a switch, if it's not one of the above, assume it belongs to cp.
                switch_list="$switch_list \"$input\""
            fi                                  
        elif ! [ -z "$input" ]; then #if it's not a switch and input is not empty, it's a path
                i=$(expr $i + 1)
                if [ ! -f "$input" -a ! -d "$input" -a "$i" -le "$count_i" ]; then 
                    err=$(expr $err + 1 ); error_list="$error_list\npath does not exit: \"b\""
                else
                    if [ "$i" -le "$count_i" ]; then 
                        eval item$i="$input" 
                        item_list="$item_list \"$input\""
                    else
                        destination="$input" #destination is last items entered
                    fi
                fi
        else
            i=0
            m=0
            n=1                     
            break
        fi      
        n=$(expr $n + 1)
done

#error condition: some or all item(s) being copied don't exist. easy_going: continue if at least one item exists, warn about rest, not easy_going: exit.
#echo "err=$err count_i=$count_i"
if [ "$easy_going" != true -a $err -gt 0 -a $err != $count_i ]; then 
    echo "Some of the paths you entered are incorrect. Script is running in strict mode and will therefore exit."
    echo -e "Bad Paths: $err $error_list"
    exit
fi

if [ $err = $count_i ]; then
    echo "ALL THE PATHS you have entered are incorrect! Exiting."
    echo -e "Bad Paths: $err $error_list"
fi

#one item to one destination:
#------------------------------
#assumes that destination is folder, it does't exist, it will create it. (so copying /a/b/c/d/firefox to /e/f/firefox will result in /e/f/firefox/firefox
#if -rename switch is given, will assume that the top element of destination path is the new name for the the item being given.

#multi-item to single destination:
#------------------------------
#assumes destination is a folder, gives error if it exists and it's a file. -rename switch will be ignored.

#ERROR CONDITIONS: 
# - multiple items being sent to a destination and it's a file.
# - if -rename switch was given and multiple items are being copied, rename switch will be ignored (easy_going). if not easy_going, exit.
# - rename option but source is folder, destination is file, exit.
# - rename option but source is file and destination is folder. easy_going: option ignored.

if [ -f "$destination" ]; then
    if [ $count_i -gt 1 ]; then 
        echo "Error: You've selected a single file as a destination and are copying multiple items to it. Exiting."; exit
    elif [ -d "$item1" ]; then
        echo "Error: Your destination is a file but your source is a folder. Exiting."; exit
    fi
fi
if [ "$rename" = true ]; then
    if [ $count_i -gt 1 ]; then
        if [ $easy_going = true ]; then
            echo "Warning: you choose the rename option but are copying multiple items. Ignoring Rename option. Continuing."
        else
            echo "Error: you choose the rename option but are copying multiple items. Script running in strict mode. Exiting."; exit
        fi
    elif [ -d "$destination" -a -f "$item1" ]; then
        echo -n "Warning: you choose the rename option but source is a file and destination is a folder with the same name. "
        if [ $easy_going = true ]; then
            echo "Ignoring Rename option. Continuing."
        else
            echo "Script running in strict mode. Exiting."; exit
        fi
    else
        dest_jr=$(dirname "$destination")
        if [ -d "$destination" ]; then item_list="$item1/*";fi
        mkdir -p "$dest_jr"
    fi
else
    mkdir -p "$destination"
fi

eval cp $switch_list $verbal $item_list "$destination"

cp_err="$?"
if [ "$cp_err" != 0 ]; then 
    echo -e "Something went wrong with the copy operation. \nExit Status: $cp_err"
else 
    echo "Copy operation exited with no errors."
fi

exit

이것은 아마도 과잉이지만 꽤 효율적이며 예상대로 작동합니다. 좋은 선생님 감사합니다.
Xedret

2

cp 여러 사용법이 있습니다.

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  or:  cp [OPTION]... SOURCE... DIRECTORY
  or:  cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

@AndyRoss의 답변은

cp SOURCE DEST

의 스타일을 cp사용하지만

cp SOURCE... DIRECTORY/

스타일 cp .

"DEST"는이 사용법에서 슬래시없이 (즉, 대상 디렉토리가 아직 존재하지 않는) 모호하다고 생각합니다. cp 이것에 대한 옵션을 추가 한 적이없는 같습니다.

다음은 dest dir에 후행 슬래시를 적용하는이 기능의 내 버전입니다.

cp-p() {
  last=${@: -1}

  if [[ $# -ge 2 && "$last" == */ ]] ; then
    # cp SOURCE... DEST/
    mkdir -p "$last" && cp "$@"
  else
    echo "cp-p: (copy, creating parent dirs)"
    echo "cp-p: Usage: cp-p SOURCE... DEST/"
  fi
}

2

방금 같은 문제가있었습니다. 내 접근 방식은 파일을 아카이브에 tar로 저장하는 것입니다.

tar cf your_archive.tar file1 /path/to/file2 path/to/even/deeper/file3

tar는 파일을 아카이브 내의 적절한 구조로 자동 저장합니다. 당신이 실행하는 경우

tar xf your_archive.tar

파일은 원하는 디렉토리 구조로 추출됩니다.


1

help_asap 및 spongeman이 위에서 제안한 것처럼 'install'명령을 사용하여 파일을 기존 디렉토리에 복사하거나 존재하지 않는 경우 새 대상 디렉토리를 작성할 수 있습니다.

옵션 1 install -D filename some/deep/directory/filename
은 파일을 새 디렉토리 나 기존 디렉토리로 복사하고 파일 이름에 기본 755 권한을 부여합니다.

옵션 1 install -D filename -m640 some/deep/directory/filename
에 따른 옵션 2 는 파일 이름 640 권한을 제공합니다.

옵션 3 install -D filename -m640 -t some/deep/directory/
에 따른 은 파일 이름을 대상 디렉토리에 대상으로하므로 파일 이름을 소스와 대상 모두에 쓸 필요는 없습니다.

옵션 3 install -D filena* -m640 -t some/deep/directory/
에 따른 옵션 4이지만 여러 파일에 와일드 카드를 사용합니다.

우분투에서 잘 작동하며 두 단계 (디렉토리 생성 및 파일 복사)를 하나의 단일 단계로 결합합니다.


1

이것은 매우 늦었지만 어딘가에 신인에게 도움이 될 수 있습니다. 폴더를 '자동'으로 만들어야하는 경우 rsync가 가장 친한 친구 여야합니다. rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /


0
rsync file /path/to/copy/file/to/is/very/deep/there

당신이 올바른 종류의을 가지고 있다면 이것은 효과가있을 수 있습니다 rsync.


이것은 나를 위해 작동하지 않습니다-나는 dest 디렉토리에 "그런 파일이나 디렉토리가 없습니다"
Rich

0

소스에서 기존 경로가 아닌 경로로 복사

mkdir p /destination && cp r /source/ $_

참고 :이 명령은 모든 파일을 복사합니다

cp –r 모든 폴더와 그 내용을 복사

$_ 마지막 명령에서 작성된 대상으로 작업


0

단순한

cp -a * /path/to/dst/

트릭을해야합니다.


그렇지 않습니다. '/ path / to / dst /'는 미리 존재해야합니다.
Shital Shah

-1

당신이 같은 것을하고 있다고 가정 해 봅시다.

cp file1.txt A / B / C / D / file.txt

여기서 A / B / C / D는 아직 존재하지 않는 디렉토리입니다.

가능한 해결책은 다음과 같습니다.

DIR=$(dirname A/B/C/D/file.txt)
# DIR= "A/B/C/D"
mkdir -p $DIR
cp file1.txt A/B/C/D/file.txt

그것이 도움이되기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.