題 如何在不重複文件夾路徑的情況下在另一個文件夾中執行命令?


有沒有 聰明 複製和移動操作的方法或複製文件的命令,而不必做 cd, 然後 mv 之後,在同一個文件夾?

例如,我必須運行以下內容:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

請注意,我正在移動文件的目錄是相同的,但我必須再次放置整個路徑,有時它會變得很煩人。我很想知道是否有另一種方法可以做到這一點 而不必再把整條路徑,因為操作將在同一路徑中完成。


72
2018-05-17 12:22


起源


我不敢相信這有很多贊成。這是重複的 unix.stackexchange.com/questions/35782/... 和 unix.stackexchange.com/questions/66889/... - user13107
@ user13107有很多方法可以提問,包括不同的措辭。如果您不知道答案被稱為“支撐擴展”,您可能無法立即找到它。 - slhck
@ user13107他們在不同的網站上,所以不重複 - Mark
馬克,謝謝,我不知道關於重複的規則。 @slhck是的。我明白。我只是感到沮喪,因為我在Unix.SE上的問題被關閉了,而且這個問題非常受歡迎。 - user13107
@ user13107,這就是你在正確的網站上發布的內容 - Samuel Edwin Ward


答案:


簡單地使用 支撐擴張

mv /folder1/folder2/folder3/{file.txt,file-2013.txt}

這相當於寫作:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

當然,Brace擴展可以提供更多參數。您甚至可以將範圍傳遞給它,例如要創建幾個測試文件夾,就可以運行了 mkdir test_{a..z},從Bash 4開始,您也可以創建零填充序列,如 touch foo{0001..3},創造 foo0001foo0002 和 foo0003。該 Bash Hackers Wiki 有一篇文章,包含幾個例子。

如果必須使用兩個不同的命令,請使用子shell和 cd 首先, 就像@ Ignacio的回答一樣


124
2018-05-17 12:29



我不知道 brace 擴展,謝謝! - Valter Silva
我試過,似乎不起作用: meniac ~: mv /tmp/f1/f2/f3/f4/f5/f6/{file.txt, file2.txt} mv: cannot stat ``/tmp/f1/f2/f3/f4/f5/f6/{file.txt,': No such file or directory - Valter Silva
你確定你正在使用Bash嗎? /bin/bash,你不是一個有腳本的腳本 /bin/sh 在shebang或其他一些不支持撐桿膨脹的外殼?如果你跑 set, 做你的 SHELLOPTS包含 braceexpand? - slhck
請注意,兩者之間應該沒有空格 file.txt, 和 file2.txt。 - slhck
你可以使它更短,以防止不改變的部分中的拼寫錯誤: mv /folder1/folder2/folder3/file{,-2013}.txt - Jan Fabry


子shell中運行該操作。

( cd /folder1/folder2/folder3 && mv file.txt file-2013.txt )

工作目錄的更改不會傳播到父shell。


74
2018-05-17 12:24



+1:我喜歡那個,比支撐擴展技巧更容易穿過貝殼(這是整潔的,但不太便攜) - Olivier Dulac
@Olivier,是什麼讓你覺得大括號擴展不便攜?你有哪個shell不支持它? - alexis
@alexis Brace擴展未由POSIX指定,因此不可移植“按設計”。 ash, dash, ksh88 更不用說舊的Bourne shell是不支持它的shell的例子。 - jlliagre
@jlliagre你提到的哪個shell完全符合POSIX標準?這意味著即使它是POSIX,它們也不會有支撐擴展。 ksh88是在POSIX批准之前;你應該升級到至少ksh93。 Linux中唯一關心的是灰燼和破折號,因為它們被用在一些小的嵌入式發行版(busybox,iirc?)和救援磁盤中。 - Kaz
@Kaz我關心shell命令的可移植性以及它們是否交互的事實並不重要。當然,你當然可以自由地不關心這一點,但請接受人們不這麼認為。你總是使用bash或支持barce-expansion的shell這一事實並不意味著每個人的情況。 - jlliagre


如果你想要聰明,這裡是bash 歷史擴張

mv /folder1/folder2/folder3/file.txt !#:1:h/file-2013.txt

我不會自己使用它,因為我發現它無法記住。我偶爾使用 vim等價物,但幾乎每次都要查一查。


21
2018-05-17 13:26





您可以設置變量。當然,這具有留下變量的副作用。

D=/folder1/folder2/folder3; mv $D/file.txt $D/file-2013.txt

11
2018-05-17 18:01



當然,通過將整個命令行放入子shell,可以避免留下變量的副作用: (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt),或只是添加一個 unset 命令結束。 (我將引號添加為“最佳實踐”;如果您養成了始終使用引號的習慣,那麼當包含特殊字符的路徑名出現時,您將不必停下來並抓撓頭部。) - Scott
@Scott如果您打算使用子殼消除副作用,那麼更容易做到 cd 而不是設置變量。不是 批量 我承認,更容易。 - Isaac Rabinovitch


我喜歡其他解決方案,但這是另一個,實現為帶有bash數組的腳本,pushd,popd:

#!/bin/bash
set -e
# from http://stackoverflow.com/a/246128/178651
script_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# paths relative to the script
relative_paths=( \
path1 \
path2 \
path3 \
path4
)

for relative_path in "${relative_paths[@]}"
do
  pushd "$script_path/$relative_path" > /dev/null 2>&1
  pwd
  mv filename1 filename2
  # could do other stuff in this directory...
  popd > /dev/null 2>&1
done

pushd "$script_path" > /dev/null 2>&1
# could do other stuff in same directory as script...
popd > /dev/null 2>&1

2
2018-05-17 20:30





Slhck以最簡單的方式直接回答了這個問題,但是Valter也喜歡autopop的答案,所以這裡有一個沿著同樣的路線;

pushd /folder1/folder2/folder3/; mv file.txt file-2013.txt; popd

1
2018-05-21 22:29