Mengapa program saya yang disebut "set" tidak dijalankan?

10

Saya telah membuat program C sederhana seperti:

int main(int argc, char *argv[]) {

    if (argc != 5) {
       fputs("Not enough arguments!\n", stderr);
       exit(EXIT_FAILURE);
    }

Dan PATH saya sudah dimodifikasi di etc / bash.bashrc seperti:

PATH=.:$PATH

Saya telah menyimpan program ini sebagai set.c dan sedang mengompilasinya

gcc -o set set.c

dalam folder

~/Programming/so

Namun, ketika saya menelepon

set 2 3

tidak ada yang terjadi. Tidak ada teks yang muncul.

Panggilan

./set 2 3

memberikan hasil yang diharapkan

Saya tidak pernah punya masalah dengan PATH sebelum dan

which set

kembali ./set. Jadi sepertinya PATH adalah yang benar. Apa yang terjadi

Ganea Dan Andrei
sumber
10
Menambahkan '.' ke PATH Anda. Lebih baik gunakan saja ./ ketika menjalankan sesuatu dari direktori lokal, atau pindahkan executable ke direktori terkenal seperti ~ / bin /
TREE
7
Ini juga merupakan ide yang buruk untuk memanggil program pengujian Anda testkarena alasan yang sama; testadalah built-in shell juga.
Jonathan Leffler
@ JonathanLeffler Namun untuk tes cepat memanggil suatu program testtampaknya masuk akal. Tentu saja pada saat Anda memasukkannya ke dalam Anda, PATHAnda benar-benar harus datang dengan nama yang berbeda. Dan sampai Anda memasukkan program PATHke dalamnya, Anda tetap harus memintanya ./test. Jadi boleh saja menggunakan nama testuntuk program selama itu adalah tes cepat yang ingin Anda hapus sebelum akhir hari.
kasperd
1
@kasperd: Sejauh yang saya ketahui, nama konvensional untuk program tes cepat adalah foo.
hmakholm tersisa Monica
Jika Anda menamainya lsmaka setiap kali Anda pergi untuk melihat apakah ada itu akan berjalan (tetapi hanya jika Anda memodifikasi jalur Anda seperti yang Anda lakukan dalam pertanyaan).
ctrl-alt-delor

Jawaban:

24

Alih-alih menggunakan which, yang tidak berfungsi saat Anda paling membutuhkannya , gunakan typeuntuk menentukan apa yang akan berjalan saat Anda mengetik perintah:

$ which set
./set
$ type set
set is a shell builtin

Shell selalu mencari bawaan sebelum mencari $PATH, jadi pengaturan $PATHtidak membantu di sini.

Akan lebih baik untuk mengubah nama executable Anda menjadi sesuatu yang lain, tetapi jika tugas Anda mengharuskan program untuk dinamai set, Anda dapat menggunakan fungsi shell:

$ function set { ./set; }
$ type set
set is a function
set ()
{
    ./set
}

(Itu berfungsi bash, tetapi cangkang lain seperti kshtidak memungkinkan. Lihat jawaban mikeserv untuk solusi yang lebih portabel.)

Sekarang mengetik setakan menjalankan fungsi bernama "set", yang dijalankan ./set. GNU bashmencari fungsi sebelum mencari builtin, dan mencari builtin sebelum mencari $PATH. Bagian bernama "PERINTAH EKSEKUSI" di halaman bash man memberikan informasi lebih lanjut tentang ini.

Lihat juga dokumentasi pada builtindan command: help builtindan help command.

yellowantphil
sumber
3
Anda merekomendasikan typelebih which, tetapi jangan memberikan alasan mengapa. ( Saya tahu mengapa , tetapi seseorang yang membutuhkan rekomendasi tidak akan melakukannya.)
cjm
1
@cjm Berikut ini adalah seluruh risalah tentang mengapa tidak yang : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan
Sangat informatif. Anda tidak akan pernah menduga ada begitu banyak kontroversi mengenai perintah yang tampaknya sederhana yang melakukan tugas sederhana
Ganea Dan Andrei
4
@GaneaDanAndrei Terutama, gunakan typesebagai ganti which, jangan beri nama program Anda "set", dan sadari bahwa itu function set { ./set; }adalah peretasan jelek yang mungkin harus Anda hindari.
yellowantphil
11

setadalah builtin di bash (dan mungkin sebagian besar shell lainnya). Ini berarti bahwa bash bahkan tidak akan mencari jalan ketika mencari fungsi.

Sebagai komentar sampingan, saya akan sangat menyarankan agar tidak menambah .jalan demi alasan keamanan. Bayangkan misalnya cding dari /tmpsetelah pengguna lain menambahkan file eksekusi /tmp/cd.

klimpergeist
sumber
2
Ya, itu ide bodoh bahwa guru saya memaksa kami untuk ,, tidak terlihat tidak profesional saat mempresentasikan program ". Dia menilai Anda jika itu tidak dilakukan.
Ganea Dan Andrei
1
Poin Brownie untuk guru-guru hebat :(
klimpergeist
4
cdadalah shell bawaan, jadi contohnya tidak akan bekerja karena alasan yang sama dengan set.
Emil Jeřábek
15
Menggunakan ./foountuk menjalankan program adalah profesional; itu menunjukkan bahwa Anda mengerti mengapa .tidak harus dalam $ PATH. Gurumu salah, dan kamu bisa memberitahunya aku bilang begitu.
zwol
10

setbukan hanya builtin, itu adalah builtin khusus POSIX . Ada beberapa perintah builtin yang ditentukan oleh standar untuk ditemukan dalam pencarian perintah sebelum hal lain - $PATHtidak dicari, nama fungsi tidak dicari, dan lain-lain. Kebanyakan builtin yang tidak spesial sebenarnya diperlukan oleh standar POSIX untuk menjadi ditemukan di shell Anda $PATH sebelum akan menjalankan salah satu prosedur builtin sendiri. Hal ini berlaku dari echodan kebanyakan orang lain (meskipun apakah standar dihormati dalam hal ini telah menjadi masalah pertentangan di kelompok terbuka milis di masa lalu) , tapi tidak dari set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Atau exec.

Semua ini adalah nama yang dilindungi dari shell, dan mereka memiliki atribut khusus selain urutan preferensi mereka untuk pencarian perintah juga. Misalnya, Anda tidak bisa mendefinisikan fungsi shell dengan nama-nama itu di shell yang sesuai standar. Ini adalah hal yang baik - ini memungkinkan orang untuk menulis skrip portabel dengan aman . Ini adalah perintah dasar dari mana penulis naskah berpengalaman dapat membangun pijakan yang aman dan dapat diandalkan di lingkungannya. Menyerang namespace ini tidak disarankan.

Namun, jika Anda ingin menyerbunya, Anda dapat melakukannya dengan nyaman alias. Urutan ekspansi shell memungkinkan ini diselesaikan. Karena aliasdiperluas ketika perintah sedang dibaca, apa pun yang Anda ganti setnamanya dengan definisi Anda akan diperluas dengan benar, mungkin saja itu tidak boleh diperluas ke salah satu dari nama-nama itu.

Jadi Anda bisa melakukan:

alias set=./set

... yang akan bekerja dengan baik.

mikeserv
sumber
3

Masalahnya adalah setshell builtin dan solusi terbaiknya adalah menggunakan nama yang berbeda untuk program yang dapat dieksekusi.

Kebetulan, minggu lalu, saya mengajukan pertanyaan tentang cara menjalankan perintah sistem alih-alih shell builtin dengan nama yang sama dan solusi yang saya terima adalah menjalankan perintah melalui env:

env set 2 3

Untuk kasus khusus ini, di mana Anda sudah tahu bahwa perintah yang ingin Anda gunakan terletak di direktori Anda saat ini, akan lebih baik untuk langsung menjalankan executable dengan memasukkan path-nya (menggunakan .untuk mewakili direktori kerja saat ini):

./set 2 3

Kedua solusi di atas adalah agnostik shell, yaitu mereka akan bekerja terlepas dari shell yang Anda gunakan.

Saran seperti menggunakan commandbuiltin tidak akan berfungsi di Bash: ini hanya mencegah fungsi shell dijalankan. Meskipun tidak terdokumentasi, saya juga memperhatikan bahwa menggunakan commandjuga menekan kata kunci shell . Namun, itu tidak akan melakukan hal yang sama untuk shell builtin seperti set. Seperti yang saya pahami, commanddapat bekerja dengan shell lain seperti zsh.

Juga, trik seperti \setatau "set"atau 'set'tidak bekerja untuk builtin Bash - meskipun mereka berguna untuk menjalankan file executable bukan alias atau shell kata kunci .

Catatan: Jawaban ini awalnya dimulai sebagai komentar atas jawaban Eric (diterima) tetapi tumbuh terlalu besar untuk dicocokkan dengan komentar. Jawaban lain yang merekomendasikan penggunaan typedan tidak menambah .PATH adalah jawaban yang bagus.

Anthony Geoghegan
sumber