Bagaimana saya bisa membuat "perintah shell" saya sendiri (mis. Mkdir / cd combo)?

41

Saya tidak tahu berapa kali saya berharap untuk sebuah perintah yang akan membuat direktori dan pindah ke direktori itu. Pada dasarnya, saya ingin yang setara dari yang berikut:

mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path

tetapi hanya harus mengetik satu /arbitrarily/long/pathkali, seperti:

mk-cd /arbitrarily/long/path

Saya mencoba membuat skrip untuk melakukan ini, tetapi hanya mengubah direktori di dalam skrip. Saya ingin direktori di shell berubah juga.

#!/bin/bash
mkdir $1
cd $1
export PWD=$PWD

Bagaimana saya bisa membuat ini bekerja?

Christopher Bottoms
sumber
12
Dengan cd, Anda memilih kasing khusus sejak awal. : D
Daniel B
2
Tidak persis apa yang saya cari, tetapi cdinfo terkait- super keren (kembali ke direktori sebelumnya menggunakan cd -, menggunakan pushddan popduntuk mempertahankan "tumpukan" direktori): superuser.com/questions/324512/…
Christopher Bottoms
8
Anda dapat mengetik mkdir -p /very/long/path, lalu menggunakan cd, spasi, dan kemudian tekan Alt + .untuk mengulangi argumen terakhir, yaitu nama dir.
choroba
2
Bukan jawaban, hanya komentar yang mirip dengan @ choroba: Anda juga dapat menggunakan mkdir -p /very/long/path; cd !#:2. String !#:2akan diperluas ke argumen nr. 2 (yaitu argumen ketiga /very/long/path, ketika penghitungan dimulai dengan nol).
Ingo Blechschmidt
3
@IngoBlechschmidt, bahkan lebih mudah, karena ini adalah argumen terakhir dari perintah terakhir, Anda bisa menggunakan !$. Saya menggunakan trik khusus ini sepanjang waktu, meskipun ada banyak lagi yang dapat Anda lakukan dengan ekspansi sejarah .
Wildcard

Jawaban:

92

Cara paling sederhana adalah dengan menggunakan fungsi shell:

mkcd() {
    mkdir -p -- "$1" && cd -- "$1"
}

Tempatkan di .bashrcfile Anda agar tersedia untuk Anda seperti perintah shell lainnya.

Alasan mengapa itu tidak berfungsi sebagai skrip eksternal adalah cdmengubah direktori saat ini dari skrip yang sedang berjalan tetapi tidak mempengaruhi yang memanggil. Ini dengan desain! Setiap proses memiliki direktori kerjanya sendiri yang diwarisi oleh anak-anaknya, tetapi sebaliknya tidak mungkin.

Kecuali jika bagian dari pipeline, berjalan di latar belakang atau secara eksplisit dalam subshell, fungsi shell tidak berjalan dalam proses yang terpisah tetapi dalam proses yang sama, seperti jika perintah telah bersumber. Shell direktori saat ini kemudian dapat diubah oleh suatu fungsi.

The &&digunakan di sini untuk memisahkan kedua perintah yang digunakan berarti, jika perintah pertama berhasil ( mkdir), menjalankan kedua ( cd). Akibatnya, jika mkdirgagal membuat direktori yang diminta, tidak ada gunanya mencoba masuk ke dalamnya. Pesan kesalahan dicetak oleh mkdirdan hanya itu.

The -ppilihan yang digunakan dengan mkdiryang ada untuk memberitahu utilitas ini untuk membuat direktori yang hilang yang merupakan bagian dari path lengkap dari nama direktori diberikan sebagai argumen. Satu efek samping adalah bahwa jika Anda meminta untuk membuat direktori yang sudah ada, mkcdfungsi tidak akan gagal dan Anda akan berakhir di direktori itu. Itu mungkin dianggap masalah atau fitur. Dalam kasus sebelumnya, fungsi dapat dimodifikasi misalnya dengan cara yang hanya memperingatkan pengguna:

mkcd() {
    if [ -d "$1" ]; then
        printf "mkcd: warning, \"%s\" already exists\n" "$1"
    else
        mkdir -p "$1" 
    fi && cd "$1"
}

Tanpa -popsi, perilaku fungsi awal akan sangat berbeda.

Jika direktori yang berisi direktori untuk membuat belum ada, mkdirgagal dan begitu pula fungsinya.

Jika direktori yang akan dibuat sudah ada, mkdirgagal juga dan cdtidak dipanggil.

Akhirnya, perhatikan bahwa pengaturan / ekspor PWDtidak ada gunanya, karena shell sudah melakukannya secara internal.

Sunting: Saya menambahkan --opsi ke kedua perintah untuk fungsi untuk memungkinkan nama direktori dimulai dengan tanda hubung.

Jlliagre
sumber
5
Anda juga harus menambahkan bahwa perintah ini tidak akan tersedia secara permanen kecuali jika ditambahkan ke ~/.bashrcatau salah satu skrip inisialisasi lainnya.
AFH
Apakah tidak ada cara di bash untuk melakukan hal yang sama dengan tcsh alias mk-cd 'mkdir -p \!:1; cd \!:1'tanpa fungsi? Atau mungkin saya harus mulai memikirkan fungsi bash lebih seperti alias yang diperluas?
Thomas Padron-McCarthy
@ ThomasPadron-McCarthy cshMekanisme berlalunya argumen ini tidak diterapkan dengan alias gaya bourne. Fungsi memang cara untuk pergi, menjadi jauh lebih fleksibel.
jlliagre
@ ThomasPadron-McCarthy - Anda mungkin ingin melihat jawaban ini , yang memang menyediakan mekanisme yang agak muskil untuk mengakses parameter yang diteruskan ke alias (dalam definisi bar). Pilihan lain adalah menggunakan history 1, seperti di alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- ini berfungsi dengan baik untuk contoh si penanya, tetapi bukan solusi umum, karena perintah lain pada baris yang sama (mis., Piping output) akan menyebabkannya gagal.
AFH
3
@ ThomasPadron-McCarthy Anda harus mulai memikirkan alias tcsh lebih mirip fungsi terbatas.
Gilles 'SO- stop being evil'
32

Saya ingin menambahkan solusi alternatif.

Script Anda akan berfungsi jika Anda memintanya dengan perintah . atau source:

. mk-cd /arbitrarily/long/path

Jika Anda melakukan ini, exportdalam skrip tidak perlu. Anda dapat memotong pengetikan .dengan menggunakan alias:

alias mk-cd='. mk-cd'

Alasan ini berfungsi adalah karena skrip biasanya berjalan di sub-shell sehingga lingkungannya hilang pada penyelesaian, tetapi perintah .(atau source) memaksanya untuk berjalan di shell saat ini.

Teknik ini dapat berguna untuk urutan perintah yang terlalu kompleks untuk suatu fungsi atau yang sedang dalam pengembangan dan sering diedit: setelah aliasdimasukkan .bash_aliases, Anda dapat mengedit skrip sesuka hati tanpa menginisialisasi ulang.

Perhatikan yang berikut ini: -

Jika Anda menulis skrip yang harus dipanggil dengan ./ sourceperintah, ada dua cara untuk memastikan hal ini: -

  1. Untuk beberapa alasan, skrip yang dipanggil dengan ./ sourcetidak perlu dieksekusi, jadi cukup hapus izin ini dan skrip tidak akan ditemukan dalam "$ PATH" atau akan memberikan kesalahan izin jika dipanggil dengan jalur eksplisit.

  2. Atau, trik berikut dapat digunakan di bagian atas skrip:

bind |& read && { echo 'Must be run from "." or "source" command'; exit 1; }

Ini bekerja karena dalam sub-shell bindmengeluarkan peringatan, meskipun tidak ada status kesalahan: karenanya readdigunakan untuk memberikan kesalahan pada input tidak.

AFH
sumber
Terima kasih atas informasinya .. Saya telah melakukan . .bashrcberkali-kali, tetapi tidak tahu persis apa yang .dilakukannya. Apakah ini mirip export?
cst1992
2
@ cst1992 - Tidak, perintahnya sama sekali berbeda: export varmenyalin variabel internal varke lingkungan, di mana ia diwariskan dan diimpor sebagai variabel di setiap sub-shell, tetapi tidak berpengaruh pada shell induk. Biasanya sub-shell dibuat untuk menjalankan skrip, dan setiap perubahan yang dibuatnya (termasuk variabel yang diekspor) hilang ketika selesai. Agar skrip mengatur variabel dalam shell, skrip harus dijalankan di dalam shell itu, dan inilah yang dilakukan .perintah: jalankan skrip tanpa membuat sub-shell. Anehnya, skrip yang dijalankan .tidak perlu memiliki izin yang dapat dieksekusi.
AFH
Terimakasih atas infonya. Saya ingin Anda memasukkan info ini ke dalam posting Anda. Ini berguna, dan terus terang, komentar tidak memiliki jaminan bahwa mereka akan ada di sana besok.
cst1992
@ cst1992 - Saya menjawab pertanyaan awal, tetapi saya tidak ingin mengacaukan jawaban saya dengan masalah tambahan. Jika Anda mencari penjelasan .dan exportperintah, judul pertanyaan ini tidak akan mengarahkan Anda untuk mengharapkan jawaban di sini, jadi saya akan membiarkan jawaban saya tetap seperti itu, tetapi terima kasih atas komentar Anda.
AFH
+1 secara umum, tetapi terutama untuk alias. Saya punya beberapa skrip yang perlu bersumber dan saya selalu lupa menjalankannya seperti itu. Alias ​​menangani itu dengan baik.
Joe
13

Bukan perintah tunggal, tetapi Anda tidak perlu mengetik ulang semua lintasan cukup gunakan !$(yang merupakan argumen terakhir dari perintah sebelumnya dalam sejarah shell).

Contoh:

mkdir -p /arbitrarily/long/path
cd !$
RiaD
sumber
1

Dengan membuat alias . Cara yang sangat populer untuk membuat perintah Anda sendiri.

Anda dapat membuat alias dengan cepat:

alias myalias='mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path'

Itu dia. Jika Anda ingin menyimpan alias itu secara permanen (bukan hanya sesi saat ini), maka letakkan perintah itu dalam sebuah dotfile (biasanya ~ / .bash_aliases atau ~ / .bash_profile).

James
sumber
4
Karena nama direktori yang panjang adalah hardcoded, alias ini tidak akan sangat berguna kecuali direktori ini secara teratur dihancurkan oleh beberapa proses lainnya.
jlliagre
1

Selain apa yang sudah dikatakan, saya ingin merekomendasikan thefuck . Ini adalah kerangka kerja Python untuk merampingkan proses shell Anda dengan memperbaiki kesalahan. Terinspirasi oleh tweet ini , yang harus Anda lakukan adalah mengetik alias Anda yang telah dikonfigurasi (secara default fuck) dan akan mencoba untuk memperbaiki perintah Anda sebelumnya. Salah satu perbaikannya berperilaku sebagai berikut:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
Duncan X Simpson
sumber