Come posso creare i miei "comandi shell" (ad esempio mkdir / cd combo)?


41

Non so quante volte ho desiderato un comando che avrebbe sia creato una directory che spostato in quella directory. Fondamentalmente, vorrei l'equivalente di quanto segue:

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

ma solo dover digitare /arbitrarily/long/pathuna volta, qualcosa del tipo:

mk-cd /arbitrarily/long/path

Ho provato a creare uno script per farlo, ma cambia solo la directory all'interno dello script. Vorrei che anche la directory nella shell fosse cambiata.

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

Come potrei riuscire a farlo funzionare?


12
Con cd, hai scelto un caso speciale fin dall'inizio. : D
Daniel B,

2
Non esattamente quello che stavo cercando, ma informazioni super cool cd(torna alla directory precedente usando cd -, usa pushde popdper mantenere una "pila" di directory): superuser.com/questions/324512/…
Christopher Bottoms

8
È possibile digitare mkdir -p /very/long/path, quindi utilizzare cd, spazio, quindi premere Alt + .per ripetere l'ultimo argomento, ovvero il nome della directory.
Choroba,

2
Non una risposta, solo un commento simile a quello di @ choroba: puoi anche usare mkdir -p /very/long/path; cd !#:2. La stringa !#:2si espanderà all'argomento nr. 2 (cioè il terzo argomento /very/long/path, poiché il conteggio inizia con zero).
Ingo Blechschmidt,

3
@IngoBlechschmidt, ancora più semplice, poiché è l'ultimo argomento dell'ultimo comando, puoi semplicemente usare !$. Uso sempre questo trucco particolare, anche se c'è molto di più che puoi fare con l'espansione della storia .
Wildcard

Risposte:


92

Il modo più semplice è usare una funzione shell:

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

Inseriscilo nel tuo .bashrcfile per renderlo disponibile come un altro comando shell.

Il motivo per cui non funziona come uno script esterno è la cdmodifica della directory corrente dello script in esecuzione ma non influisce su quella chiamante. Questo è di design! Ogni processo ha una propria directory di lavoro che è ereditata dai suoi figli, ma non è possibile il contrario.

A meno che parte di una pipeline, eseguita in background o esplicitamente in una subshell, una funzione di shell non venga eseguita in un processo separato ma nella stessa, proprio come se il comando è stato fornito. La shell di directory corrente può quindi essere modificata da una funzione.

Il &&qui usato per separare entrambi i comandi utilizzati significa, se il primo comando riesce ( mkdir), esegui il secondo ( cd). Di conseguenza, se mkdirnon è possibile creare la directory richiesta, non ha senso tentare di accedervi. Viene stampato un messaggio di errore mkdire basta.

L' -popzione usata con mkdirè lì per dire a questa utility di creare qualsiasi directory mancante che fa parte del percorso completo del nome della directory passato come argomento. Un effetto collaterale è che se chiedi di creare una directory già esistente, la mkcdfunzione non fallirà e finirai in quella directory. Potrebbe essere considerato un problema o una funzionalità. Nel primo caso, la funzione può essere modificata ad esempio in quel modo che avverte semplicemente l'utente:

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

Senza l' -popzione, il comportamento della funzione iniziale sarebbe stato molto diverso.

Se la directory che contiene la directory da creare non esiste già, mkdirla funzione non funziona.

Se la directory da creare esiste già, mkdirfallisce anche e cdnon viene chiamata.

Infine, nota che l'impostazione / esportazione PWDè inutile, poiché la shell lo fa già internamente.

Modifica: ho aggiunto l' --opzione a entrambi i comandi per la funzione per consentire un nome di directory che inizia con un trattino.


5
È inoltre necessario aggiungere che questo comando non sarà disponibile in modo permanente a meno che non venga aggiunto ~/.bashrco uno degli altri script di inizializzazione.
AFH,

Non c'è modo in bash di fare l'equivalente di tcsh alias mk-cd 'mkdir -p \!:1; cd \!:1'senza una funzione? O forse dovrei iniziare a pensare alle funzioni bash più come alias estesi?
Thomas Padron-McCarthy,

@ ThomasPadron-McCarthy Questo cshmeccanismo di passaggio degli argomenti non è implementato con alias stile bourne. Le funzioni sono davvero la strada da percorrere, essendo molto più flessibili.
jlliagre,

@ ThomasPadron-McCarthy - Potresti guardare questa risposta , che fornisce un meccanismo piuttosto astruso per accedere ai parametri passati ad un alias (nella definizione di bar). Un'altra opzione sarebbe quella di usare history 1, come in alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- questo funziona benissimo per l'esempio dell'interrogatore, ma non è una soluzione generale, poiché qualsiasi altro comando sulla stessa linea (ad esempio il piping dell'output) causerà il fallimento.
AFH,

3
@ ThomasPadron-McCarthy Dovresti iniziare a pensare agli alias di TCS più come funzioni limitate.
Gilles 'SO- smetti di essere malvagio'

32

Vorrei aggiungere una soluzione alternativa.

Lo script sarà funzionerà se si richiama con il . o sourcecomando:

. mk-cd /arbitrarily/long/path

Se lo fai, exportlo script non è necessario. Puoi bypassare digitando il .usando un alias:

alias mk-cd='. mk-cd'

Il motivo per cui funziona è che uno script normalmente viene eseguito in una sotto-shell, quindi il suo ambiente viene perso al completamento, ma il comando .(o source) lo costringe a funzionare nella shell corrente.

Questa tecnica può essere utile per sequenze di comandi troppo complesse per una funzione o che sono in fase di sviluppo e vengono frequentemente modificate: una volta aliasinserito .bash_aliases, è possibile modificare lo script a piacimento senza reinizializzarlo.

Si noti quanto segue: -

Se si scrive uno script che deve essere chiamato con il comando ./ source, ci sono due modi per assicurarsi che:

  1. Per qualche motivo, uno script invocato con ./ sourcenon deve essere eseguibile, quindi è sufficiente rimuovere questa autorizzazione e lo script non sarà trovato in "$ PATH" o darà un errore di autorizzazione se invocato con un percorso esplicito.

  2. In alternativa, è possibile utilizzare il seguente trucco all'inizio della sceneggiatura:

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

Questo funziona perché in una sotto-shell bindemette un avviso, sebbene non sia presente uno stato di errore: pertanto readviene utilizzato per fornire un errore in assenza di input.


Grazie per le informazioni su .. Ho fatto . .bashrcinnumerevoli volte, ma non sapevo esattamente cosa facesse .. È simile a export?
cst1992,

2
@ cst1992 - No, i comandi sono completamente diversi: export varcopia la variabile interna varnell'ambiente, dove viene ereditata e importata come variabile in qualsiasi sotto-shell, ma non ha alcun effetto su una shell genitore. Normalmente viene creata una sotto-shell per eseguire uno script e tutte le modifiche apportate (incluse le variabili esportate) vanno perse al termine. Affinché uno script imposti le variabili in una shell, deve essere eseguito in quella shell, ed è quello che fa il .comando: eseguire lo script senza creare una sotto-shell. Curiosamente, gli script eseguiti da .non necessitano dell'autorizzazione eseguibile.
AFH,

Grazie per le informazioni. Insisto affinché tu includa queste informazioni nel tuo post. Questo è utile e, francamente, i commenti non hanno alcuna garanzia che saranno presenti domani.
cst1992,

@ cst1992 - Ho affrontato la domanda originale, ma non voglio ingombrare la mia risposta con problemi accessori. Se stavi cercando una spiegazione dei comandi .e export, il titolo di questa domanda non ti indurrebbe ad aspettarti una risposta qui, quindi lascerò la mia risposta così com'è, ma grazie per il tuo commento.
AFH,

+1 in generale, ma soprattutto per l'alias. Ho alcuni script che devono essere forniti e dimentico sempre di eseguirli in quel modo. L'alias si prende cura di questo bene.
Joe,

13

Non è un singolo comando, ma non è necessario riscrivere tutto il percorso utilizzato !$(che è l'ultimo argomento del comando precedente nella cronologia della shell).

Esempio:

mkdir -p /arbitrarily/long/path
cd !$

1

Creando alias . Modo molto popolare per creare i tuoi comandi.

Puoi creare un alias al volo:

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

Questo è tutto. Se vuoi mantenere quell'alias in modo permanente (non solo la sessione corrente), metti quel comando in un dotfile (in genere ~ / .bash_aliases o ~ / .bash_profile).


4
Poiché il nome della directory lunga è codificato, questo alias non sarebbe molto utile se questa directory non venisse regolarmente distrutta da altri processi.
jlliagre,

1

Oltre a quanto è già stato suggerito, vorrei consigliare Thefuck . È un framework Python per semplificare il processo della shell correggendo gli errori. Ispirato da questo tweet , tutto ciò che devi fare è digitare l'alias configurato (per impostazione predefinita fuck) e tenterà di correggere il tuo comando precedente. Una delle sue correzioni si comporta come segue:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.