41

Я не могу сказать, сколько раз я хотел получить команду, которая одновременно создала бы каталог и переместилась бы в этот каталог. В принципе, я хотел бы эквивалент следующего:

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

но нужно только один раз набрать /arbitrarily/long/path , что-то вроде:

mk-cd /arbitrarily/long/path

Я попытался создать сценарий, чтобы сделать это, но он только изменяет каталог в сценарии. Я бы хотел, чтобы каталог в оболочке также изменился.

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

Как я мог бы сделать эту работу?

5 ответов5

92

Самый простой способ - использовать функцию оболочки:

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

Поместите его в свой файл .bashrc чтобы сделать его доступным вам, как и другую команду оболочки.

Причина, по которой он не работает как внешний скрипт, заключается в том, что cd изменяет текущий каталог запущенного скрипта, но не влияет на вызывающий. Это по замыслу! Каждый процесс имеет свой собственный рабочий каталог, который наследуется его дочерними элементами, но обратное невозможно.

Если только часть конвейера не запущена в фоновом режиме или явно в подоболочке, функция оболочки запускается не в отдельном процессе, а в том же самом, как если бы команда была получена. Текущая оболочка каталога может быть изменена функцией.

Используемый здесь && для разделения обеих используемых команд означает, что, если первая команда выполнена успешно (mkdir), запустите вторую (cd). Следовательно, если mkdir не удается создать запрошенный каталог, нет смысла пытаться зайти в него. Сообщение об ошибке печатается mkdir и это все .

Параметр -p используемый с mkdir позволяет этой утилите создать любой отсутствующий каталог, который является частью полного пути имени каталога, переданного в качестве аргумента. Одним из побочных эффектов является то, что если вы попросите создать уже существующий каталог, функция mkcd не будет работать, и вы окажетесь в этом каталоге. Это может рассматриваться как проблема или особенность. В первом случае функция может быть изменена, например, таким способом, который просто предупреждает пользователя:

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

Без опции -p поведение исходной функции было бы совсем другим.

Если каталог, содержащий каталог для создания, еще не существует, mkdir завершается ошибкой, так же как и функция.

Если каталог для создания уже существует, mkdir тоже не работает и cd не вызывается.

Наконец, обратите внимание, что установка / экспорт PWD имеет смысла, так как оболочка уже делает это внутренне.

Редактировать: я добавил опцию -- в обе команды для функции, чтобы имя каталога начиналось с тире.

32

Я хотел бы добавить альтернативное решение.

Ваш скрипт будет работать, если вы вызовете его с помощью . или source команда:

. mk-cd /arbitrarily/long/path

Если вы сделаете это, export в сценарии не требуется. Вы можете обойти ввод . используя alias:

alias mk-cd='. mk-cd'

Причина, по которой это работает, заключается в том, что скрипт обычно выполняется в под-оболочке, поэтому его среда теряется при завершении, кроме . (или source) команда заставляет его работать в текущей оболочке.

Этот метод может быть полезен для последовательностей команд, которые слишком сложны для функции или находятся в стадии разработки и часто редактируются: после ввода alias в .bash_aliases вы можете редактировать скрипт по своему усмотрению без повторной инициализации.

Обратите внимание на следующее:-

Если вы пишете скрипт, который должен быть вызван с ./source команда, есть два способа убедиться в этом:-

  1. По какой-то причине скрипт вызывается с помощью ./source не обязательно должен быть исполняемым, поэтому просто удалите это разрешение, и сценарий либо не будет найден в «$ PATH», либо выдаст ошибку разрешения, если будет вызван с явным путем.

  2. В качестве альтернативы можно использовать следующий трюк в начале скрипта:

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

Это работает, потому что при bind под-оболочки выдает предупреждение, хотя нет статуса ошибки: следовательно, read используется, чтобы выдать ошибку при отсутствии ввода.

13

Ни одной команды, но вам не нужно перепечатывать весь путь, просто используйте !$ (последний аргумент предыдущей команды в истории оболочки).

Пример:

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

В дополнение к тому, что уже было предложено, я хотел бы порекомендовать the fuck. Это среда Python для оптимизации процесса оболочки путем исправления ошибок. Вдохновленный этим сообщением, все, что вам нужно сделать, это ввести настроенный псевдоним (по умолчанию, fuck), и он попытается исправить вашу предыдущую команду. Одно из его исправлений ведет себя следующим образом:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
1

Создавая псевдонимы. Очень популярный способ создавать свои собственные команды.

Вы можете создать псевдоним на лету:

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

Вот и все. Если вы хотите сохранить этот псевдоним постоянно (а не только в текущем сеансе), поместите эту команду в точечный файл (обычно ~/.bash_aliases или ~/.bash_profile).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .