Pertanyaan Buat alias Bash yang mengambil parameter?


Saya terbiasa menggunakan CShell (), yang memungkinkan Anda membuat alias yang mengambil parameter. Notasi itu seperti sesuatu

alias junk="mv \\!* ~/.Trash"

Di Bash, ini sepertinya tidak berhasil. Mengingat Bash memiliki banyak fitur yang berguna, saya akan berasumsi bahwa ini telah diimplementasikan tetapi saya bertanya-tanya bagaimana caranya.


866
2017-08-20 12:11


asal


Jawaban:


Alias ​​Bash tidak langsung menerima parameter. Anda harus membuat fungsi dan alias itu.

alias tidak menerima parameter tetapi fungsi dapat disebut seperti alias. Sebagai contoh:

myfunction() {
    #do things with parameters like $1 such as
    mv "$1" "$1.bak"
    cp "$2" "$1"
}


myfunction old.conf new.conf #calls `myfunction`

By the way, fungsi Bash didefinisikan dalam Anda .bashrc dan file lain tersedia sebagai perintah di dalam shell Anda. Jadi misalnya Anda dapat memanggil fungsi sebelumnya seperti ini

$ myfunction original.conf my.conf

1500
2017-08-20 12:15



Dengan memperbaiki jawaban di atas, Anda bisa mendapatkan sintaks 1-baris seperti yang Anda bisa untuk alias, yang lebih nyaman untuk definisi ad-hoc dalam file shell atau .bashrc:

bash$ myfunction() { mv "$1" "$1.bak"; cp "$2" "$1"; }

bash$ myfunction original.conf my.conf

Jangan lupakan titik koma di depan kurung tutup kanan. Demikian pula, untuk pertanyaan sebenarnya:

csh% alias junk="mv \\!* ~/.Trash"

bash$ junk() { mv "$@" ~/.Trash/; }

Atau:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }

139
2018-05-14 15:46



Pertanyaannya hanya bertanya salah. Anda tidak membuat alias yang mengambil parameter karena alias tambahkan saja nama kedua untuk sesuatu yang sudah ada. Fungsi yang diinginkan OP adalah function perintah untuk membuat fungsi baru. Anda tidak perlu melakukan alias fungsi karena fungsi sudah memiliki nama.

Saya pikir Anda menginginkan sesuatu seperti ini:

function trash() { mv "$@" ~/.Trash; }

Itu dia! Anda dapat menggunakan parameter $ 1, $ 2, $ 3, dll, atau hanya memasukkan semuanya dengan $ @


77
2018-01-08 06:35



TL; DR: Lakukan ini sebagai gantinya

Ini jauh lebih mudah dan lebih mudah dibaca untuk menggunakan fungsi daripada alias untuk menempatkan argumen di tengah-tengah perintah.

$ wrap_args() { echo "before $@ after"; }
$ wrap_args 1 2 3
before 1 2 3 after

Jika Anda terus membaca, Anda akan mempelajari hal-hal yang tidak perlu Anda ketahui tentang pemrosesan argumen shell. Pengetahuan itu berbahaya. Dapatkan hasil yang Anda inginkan, sebelum sisi gelap selamanya mengontrol nasib Anda.

Klarifikasi

bash alias melakukan menerima argumen, tetapi hanya di akhir:

$ alias speak=echo
$ speak hello world
hello world

Menempatkan argumen ke dalam tengah perintah melalui alias memang mungkin tetapi menjadi jelek.

Jangan coba ini di rumah, kiddies!

Jika Anda suka menghindari batasan dan melakukan apa yang orang lain katakan tidak mungkin, inilah resepnya. Jangan salahkan saya jika rambut Anda menjadi lelah dan wajah Anda tertutup oleh gaya ilmuwan gila.

Solusinya adalah menyampaikan argumen itu alias hanya menerima di akhir untuk pembungkus yang akan memasukkan mereka di tengah dan kemudian jalankan perintah Anda.

Solusi 1

Jika Anda benar-benar menentang penggunaan fungsi per se, Anda dapat menggunakan:

$ alias wrap_args='f(){ echo before "$@" after;  unset -f f; }; f'
$ wrap_args x y z
before x y z after

Anda dapat mengganti $@ dengan $1 jika Anda hanya menginginkan argumen pertama.

Penjelasan 1

Ini menciptakan fungsi sementara f, yang melewati argumen (perhatikan itu f disebut di bagian paling akhir). Itu unset -f menghapus definisi fungsi sebagai alias dijalankan sehingga tidak bertahan setelah itu.

Solusi 2

Anda juga dapat menggunakan subkulit:

$ alias wrap_args='sh -c '\''echo before "$@" after'\'' _'

Penjelasan 2

Alias ​​membangun perintah seperti:

sh -c 'echo before "$@" after' _

Komentar:

  • Placeholder _ diperlukan, tetapi bisa jadi apa saja. Sudah diatur untuk sh's $0, dan diperlukan agar argumen pertama yang diberikan pengguna tidak terpakai. Demonstrasi:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • Tanda kutip tunggal di dalam tanda kutip tunggal diperlukan. Berikut ini contohnya tidak berfungsi dengan tanda kutip ganda:

    $ sh -c "echo Consumed: $0 Printing: $@" alcohol drunken babble
    Consumed: -bash Printing:
    

    Di sini nilai-nilai dari shell interaktif $0 dan $@ diganti menjadi kutipan ganda sebelum itu diteruskan ke sh. Inilah buktinya:

    echo "Consumed: $0 Printing: $@"
    Consumed: -bash Printing:
    

    Tanda kutip tunggal memastikan bahwa variabel-variabel ini tidak ditafsirkan oleh shell interaktif, dan diteruskan secara harfiah ke sh -c.

    Anda bisa menggunakan tanda kutip ganda dan \$@, tetapi praktik terbaik adalah mengutip argumen Anda (karena mungkin berisi spasi), dan \"\$@\" terlihat lebih buruk, tetapi dapat membantu Anda memenangkan kontes obfuscation di mana rambut yang lelah merupakan prasyarat untuk masuk.


58
2018-02-26 08:40



Solusi alternatif adalah menggunakan spidol, alat yang saya buat baru-baru ini yang memungkinkan Anda untuk "membookmark" templat perintah dan dengan mudah menempatkan kursor di tempat-tempat perintah:

commandline marker

Saya menemukan bahwa sebagian besar waktu, saya menggunakan fungsi shell sehingga saya tidak perlu menulis perintah yang sering digunakan lagi dan lagi di baris perintah. Masalah penggunaan fungsi untuk kasus penggunaan ini, adalah menambahkan istilah baru ke kosakata perintah saya dan harus mengingat parameter fungsi apa yang mengacu pada perintah-nyata. Tujuan penanda adalah untuk menghilangkan beban mental itu.


26
2018-05-22 12:39



Berikut adalah tiga contoh fungsi yang saya miliki di ~/.bashrc, itu pada dasarnya alias yang menerima parameter:

#Utility required by all below functions.
#https://stackoverflow.com/questions/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"

.

:<<COMMENT
    Alias function for recursive deletion, with are-you-sure prompt.

    Example:
        srf /home/myusername/django_files/rest_tutorial/rest_venv/

    Parameter is required, and must be at least one non-whitespace character.

    Short description: Stored in SRF_DESC

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*rm -r*:srf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - y/n prompt: https://stackoverflow.com/a/3232082/2736496
    - Alias w/param: https://stackoverflow.com/a/7131683/2736496
COMMENT
#SRF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
SRF_DESC="srf [path]: Recursive deletion, with y/n prompt\n"
srf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    #Actual line-breaks required in order to expand the variable.
    #- https://stackoverflow.com/a/4296147/2736496
    read -r -p "About to
    sudo rm -rf \"$param\"
Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        sudo rm -rf "$param"
    else
        echo "Cancelled."
    fi
}

.

:<<COMMENT
    Delete item from history based on its line number. No prompt.

    Short description: Stored in HX_DESC

    Examples
        hx 112
        hx 3

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HX_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HX_DESC="hx [linenum]: Delete history item at line number\n"
hx()  {
    history -d "$1"
}

.

:<<COMMENT
    Deletes all lines from the history that match a search string, with a
    prompt. The history file is then reloaded into memory.

    Short description: Stored in HXF_DESC

    Examples
        hxf "rm -rf"
        hxf ^source

    Parameter is required, and must be at least one non-whitespace character.

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*hxf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HXF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HXF_DESC="hxf [searchterm]: Delete all history items matching search term, with y/n prompt\n"
hxf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    read -r -p "About to delete all items from history that match \"$param\". Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        #Delete all matched items from the file, and duplicate it to a temp
        #location.
        grep -v "$param" "$HISTFILE" > /tmp/history

        #Clear all items in the current sessions history (in memory). This
        #empties out $HISTFILE.
        history -c

        #Overwrite the actual history file with the temp one.
        mv /tmp/history "$HISTFILE"

        #Now reload it.
        history -r "$HISTFILE"     #Alternative: exec bash
    else
        echo "Cancelled."
    fi
}

Referensi:


7
2018-01-13 03:57



NB: Jika ide ini tidak jelas, itu adalah ide yang buruk untuk menggunakan alias untuk apa pun selain alias, yang pertama adalah 'fungsi dalam alias' dan yang kedua adalah 'sulit untuk membaca redirect / sumber'. Juga, ada kekurangan (yang saya pikir akan menjadi jelas, tetapi kalau-kalau Anda bingung: Saya tidak bermaksud mereka untuk benar-benar digunakan ... di mana saja!)

.................................................. .................................................. ............................................

Saya telah menjawab ini sebelumnya, dan selalu seperti ini di masa lalu:

alias foo='__foo() { unset -f $0; echo "arg1 for foo=$1"; }; __foo()'

yang bagus dan bagus, kecuali Anda menghindari penggunaan fungsi bersama-sama. dalam hal ini Anda dapat memanfaatkan kemampuan luas bash untuk mengarahkan teks:

alias bar='cat <<< '\''echo arg1 for bar=$1'\'' | source /dev/stdin'

Keduanya sama panjangnya memberi atau mengambil beberapa karakter.

Itu nyata kicker adalah perbedaan waktu, yang paling atas adalah 'metode fungsi' dan bagian bawahnya adalah metode 'redirect-source'. Untuk membuktikan teori ini, waktu berbicara untuk dirinya sendiri:

arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.004s sys 0m0.008s  # <--time spent in foo
 real 0m0.000s user 0m0.000s sys 0m0.000s  # <--time spent in bar
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.000s sys 0m0.012s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.012s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.008s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE

Ini adalah bagian bawah dari sekitar 200 hasil, yang dilakukan secara acak. Tampaknya pembuatan / perusakan fungsi membutuhkan waktu lebih lama daripada pengarahan ulang. Semoga ini akan membantu pengunjung masa depan untuk pertanyaan ini (tidak ingin menyimpannya untuk diri saya sendiri).


4
2018-05-20 18:39



Untuk mengambil parameter, Anda harus menggunakan fungsi!

Namun, $ @ diinterpretasikan saat membuat alias, bukan selama eksekusi alias dan keluar dari $, juga tidak berhasil. Bagaimana saya mengatasi masalah ini?

Anda perlu menggunakan fungsi shell sebagai ganti alias untuk menyingkirkan masalah ini. Anda dapat mendefinisikan foo sebagai berikut:

function foo() { /path/to/command "$@" ;}

ATAU

foo() { /path/to/command "$@" ;}

Akhirnya, panggil foo Anda () menggunakan sintaks berikut:

foo arg1 arg2 argN

Pastikan Anda menambahkan foo () ke ~/.bash_profile atau ~/.zshrc mengajukan.

Dalam kasus Anda, ini akan berhasil

function trash() { mv $@ ~/.Trash; }

2
2017-07-28 09:15



Ada alasan teknis yang sah untuk menginginkan solusi umum untuk masalah bash alias tidak memiliki mekanisme untuk mengambil reposisi argumen sewenang-wenang. Salah satu alasannya adalah jika perintah yang ingin Anda jalankan akan terpengaruh oleh perubahan lingkungan yang dihasilkan dari menjalankan suatu fungsi. Secara keseluruhan lain kasus, fungsi harus digunakan.

Apa yang baru-baru ini memaksa saya untuk mencoba solusi untuk ini adalah bahwa saya ingin membuat beberapa perintah yang disingkat untuk mencetak definisi variabel dan fungsi. Jadi saya menulis beberapa fungsi untuk tujuan itu. Namun, ada variabel tertentu yang (atau mungkin) diubah oleh fungsi panggilan itu sendiri. Diantaranya adalah:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

Perintah dasar yang saya gunakan (dalam fungsi) untuk mencetak variabel defns. dalam bentuk keluaran dengan perintah yang ditetapkan adalah:

sv () { set | grep --color=never -- "^$1=.*"; }

Misalnya.:

> V=voodoo
sv V
V=voodoo

Masalah: Ini tidak akan mencetak definisi variabel yang disebutkan di atas karena mereka berada dalam konteks saat ini, misalnya, jika dalam prompt shell interaktif (atau tidak dalam panggilan fungsi apa pun), FUNCNAME tidak didefinisikan. Tetapi fungsi saya memberi tahu saya informasi yang salah:

> sv FUNCNAME
FUNCNAME=([0]="sv")

Satu solusi yang saya temukan telah disebutkan oleh orang lain di posting lain tentang topik ini. Untuk perintah khusus ini untuk mencetak variabel defns., Dan yang hanya membutuhkan satu argumen, saya melakukan ini:

alias asv='(grep -- "^$(cat -)=.*" <(set)) <<<'

Yang memberikan output yang benar (tidak ada), dan status hasil (salah):

> asv FUNCNAME
> echo $?
1

Namun, saya masih merasa terdorong untuk menemukan solusi yang berfungsi untuk sejumlah argumen yang acak.

Solusi Umum Untuk Meloloskan Argumen-argumen Sewenang-wenang kepada Sebuah Perintah Bash Aliased:

# (I put this code in a file "alias-arg.sh"):

# cmd [arg1 ...] – an experimental command that optionally takes args,
# which are printed as "cmd(arg1 ...)"
#
# Also sets global variable "CMD_DONE" to "true".
#
cmd () { echo "cmd($@)"; declare -g CMD_DONE=true; }

# Now set up an alias "ac2" that passes to cmd two arguments placed
# after the alias, but passes them to cmd with their order reversed:
#
# ac2 cmd_arg2 cmd_arg1 – calls "cmd" as: "cmd cmd_arg1 cmd_arg2"
#
alias ac2='
    # Set up cmd to be execed after f() finishes:
    #
    trap '\''cmd "${CMD_ARGV[1]}" "${CMD_ARGV[0]}"'\'' SIGUSR1;
    #        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #       (^This is the actually execed command^)
    #
    # f [arg0 arg1 ...] – acquires args and sets up trap to run cmd:
    f () {
        declare -ag CMD_ARGV=("$@");  # array to give args to cmd
        kill -SIGUSR1 $$;             # this causes cmd to be run
        trap SIGUSR1;                 # unset the trap for SIGUSR1
        unset CMD_ARGV;               # clean up env...
        unset f;                      # incl. this function!
    };
    f'  # Finally, exec f, which will receive the args following "ac2".

Misalnya.:

> . alias-arg.sh
> ac2 one two
cmd(two one)
>
> # Check to see that command run via trap affects this environment:
> asv CMD_DONE
CMD_DONE=true

Hal yang baik tentang solusi ini adalah bahwa semua trik khusus yang digunakan untuk menangani parameter posisi (argumen) ke perintah akan bekerja saat menyusun perintah yang terperangkap. Satu-satunya perbedaan adalah bahwa sintaks array harus digunakan.

Misalnya.,

Jika Anda ingin "$ @", gunakan "$ {CMD_ARGV [@]}".

Jika Anda ingin "$ #", gunakan "$ {# CMD_ARGV [@]}".

Dan lain-lain


2
2017-10-18 17:35