Bagaimana cara melakukan perintah di folder lain, tanpa mengulangi jalur folder?

73

Apakah ada cara cerdas untuk melakukan salin dan memindahkan operasi atau perintah untuk menduplikasi file, tanpa harus melakukan cd, lalu mvsetelah itu, pada folder yang sama?

Misalnya, saya harus menjalankan yang berikut ini:

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

Perhatikan bahwa direktori tempat saya memindahkan file adalah sama, tetapi saya harus meletakkan seluruh path lagi dan kadang-kadang menjengkelkan. Saya ingin tahu apakah ada cara lain untuk melakukan itu tanpa harus meletakkan seluruh jalan lagi , karena operasi akan dilakukan di jalur yang sama.

Valter Silva
sumber
9
Saya tidak percaya ini memiliki begitu banyak upvotes. Ini adalah duplikat unix.stackexchange.com/questions/35782/… dan unix.stackexchange.com/questions/66889/…
user13107
13
@ user13107 Ada banyak cara untuk mengajukan pertanyaan, termasuk kata-kata yang berbeda. Dan jika Anda tidak tahu bahwa jawabannya disebut "ekspansi brace", Anda mungkin tidak dapat langsung menemukannya.
slhck
2
@ user13107 mereka berada di situs yang berbeda sehingga tidak duplikat
user151019
1
Mark, Terima kasih, saya tidak tahu aturan tentang duplikat. Sslhck Ya. Saya mengerti. Saya hanya frustrasi karena pertanyaan saya tentang Unix.SE ditutup sebagai duplikat dan yang ini menjadi sangat populer.
user13107
3
@ user13107, itulah yang Anda dapatkan untuk memposting di situs yang tepat
Samuel Edwin Ward

Jawaban:

125

Cukup gunakan ekspansi brace :

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

Ini sama dengan menulis:

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

Ekspansi Brace memungkinkan Anda memberikan lebih banyak argumen, tentu saja. Anda bahkan dapat melewatkan rentang untuk itu, misalnya untuk membuat beberapa folder tes, Anda dapat menjalankan mkdir test_{a..z}, dan mulai dengan Bash 4, Anda dapat membuat urutan nol-empuk juga, seperti dalam touch foo{0001..3}, yang membuat foo0001, foo0002dan foo0003. The Bash Hacker Wiki memiliki sebuah artikel dengan beberapa contoh untuk Anda.

Jika Anda harus menggunakan dua perintah yang berbeda, gunakan subkulit dan di cdsana terlebih dahulu, seperti pada jawaban @ Ignacio .

slhck
sumber
5
Saya tidak tahu tentang braceekspansi, terima kasih!
Valter Silva
Saya mencoba dan tampaknya tidak berhasil: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
5
Apakah Anda yakin menggunakan Bash, seperti pada /bin/bash, dan Anda tidak ada dalam skrip yang ada /bin/shdi shebang atau shell lain yang tidak mendukung ekspansi brace? Jika Anda menjalankan set, jangan Anda SHELLOPTSmengandung braceexpand?
slhck
22
Perhatikan bahwa seharusnya tidak ada ruang antara file.txt,dan file2.txt.
slhck
9
Anda dapat membuatnya lebih pendek, untuk mencegah kesalahan ketik di bagian yang tidak berubah:mv /folder1/folder2/folder3/file{,-2013}.txt
Jan Fabry
74

Jalankan operasi dalam subkulit.

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

Perubahan direktori kerja tidak akan disebarkan ke shell induk.

Ignacio Vazquez-Abrams
sumber
11
+1: Saya suka yang itu, lebih portabel di seluruh cangkang daripada trik brace-expansion (yang rapi, tapi kurang portabel)
Olivier Dulac
@ Olivier, apa yang membuat Anda berpikir brace ekspansi tidak portabel? Cangkang mana yang ada dalam pikiran Anda yang tidak mendukungnya?
alexis
5
@alexis Ekspansi Brace tidak ditentukan oleh POSIX sehingga tidak portabel "dengan desain". ash, dash, ksh88Belum lagi shell Bourne tua contoh kerang tidak mendukung itu.
jlliagre
@ jlliagre Cangkang mana yang Anda sebutkan yang sepenuhnya POSIX? Itu berarti mereka tidak akan memiliki ekspansi brace bahkan jika itu POSIX. ksh88 sebelum POSIX disahkan; Anda harus meningkatkan ke setidaknya ksh93. Dua orang yang peduli pada Linux adalah ash dan dash karena mereka digunakan di beberapa distro kecil yang disematkan (busybox, iirc?) Dan disk penyelamat.
Kaz
2
@ Ka, saya peduli tentang portabilitas perintah shell dan fakta bahwa perintah itu interaktif atau tidak, tidak masalah. Tentu saja, Anda tentu saja bebas untuk tidak peduli tentang hal ini tetapi tolong terima bahwa orang berpikir sebaliknya. Fakta bahwa Anda selalu menggunakan bash atau shell yang mendukung ekspansi barce tidak berarti itu kasus semua orang.
jlliagre
21

Jika Anda ingin pintar, inilah bash histori ekspansi

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

Saya tidak akan menggunakan ini sendiri karena saya merasa tidak mungkin untuk menghafal. Saya kadang-kadang menggunakan setara vim , tetapi harus mencarinya hampir setiap waktu.

glenn jackman
sumber
11

Anda dapat mengatur variabel. Tentu saja ini memiliki efek samping meninggalkan variabel di sekitar.

D=/folder1/folder2/folder3; mv $D/file.txt $D/file-2013.txt
sjbotha
sumber
Dan, tentu saja, Anda dapat menghindari efek samping dari meninggalkan variabel (s) sekitar dengan menempatkan seluruh baris perintah ke dalam subkulit:, (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt)atau hanya dengan menambahkan unsetperintah di akhir. (Saya menambahkan tanda kutip sebagai ”praktik terbaik”; jika Anda terbiasa selalu menggunakan tanda kutip, Anda tidak perlu berhenti dan menggaruk kepala saat nama path yang berisi karakter khusus muncul.)
Scott
@ Hemat jika Anda akan menggunakan subkulit untuk menghilangkan efek samping, lebih mudah untuk melakukan cddaripada mengatur variabel. Tidak banyak lebih mudah, saya akui.
Isaac Rabinovitch
2

Saya suka solusi lain, tapi ini yang lain, diimplementasikan sebagai skrip dengan array 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
Gary S. Weaver
sumber
1

Slhck langsung menjawab pertanyaan dengan cara yang sesederhana mungkin, tetapi Valter juga menyukai jawaban autopop, jadi inilah salah satu yang sejalan;

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