Выполнить команду из другого каталога в bash

117

Скажи, что я делаю это:

cd subdir
git init
cd ../

Есть ли способ сделать это с помощью одной команды или, возможно, двух, вместо того, чтобы входить и выходить из каталога, чтобы выполнить там команду?

(Не искать специфическое для git решение; это всего лишь пример.)

Тревор Бернхэм
источник

Ответы:

200

Часто это лучший способ:

( cd dir ; git init )

или же

( cd dir && git init )

Это довольно короткий и легко набрать. Он запускает под-оболочку, поэтому вы не можете изменить свою среду, но это, похоже, не проблема.

Мат
источник
1
И если вам нужно установить переменную среды или две, просто добавьте это как другую команду в начале.
Бегемот
1
@CraigMcQueen: не совсем. $?будет содержать код завершения последней команды, которая выполнялась в подоболочке. Если вы используете &&вариант (который вы обычно должны), то вы получите код выхода первой команды, которая не прошла (или 0, если все прошло нормально).
Мат
Я думаю, что скобки необязательны
Francis.Beauchamp
10
@ Francis.Beauchamp: если вы опустите парены, вы будете в поддире после команды, а не назад, где вы начали.
Мат
Как это сделать в Windows?
Карл Моррисон
22

Я искал способ выполнить команду git из пути и внести изменения в хранилище по другому пути. Так что я оказался в этом вопросе здесь.

Но для моих конкретных нужд не помог ни принятый ответ, ни какой-либо другой.

Мне нужно было запускать команды git sudo -u USER /usr/bin/git(другой пользователь запускает его). И, как вы, возможно, знаете, sudo не позволяет мне запускать cdкоманду, поэтому я не могу находиться в каталоге репозитория.

Итак, я пошел на страницу руководства Git . И среди вариантов я увидел --git-dir=<path>:

--git-Dir =

Установите путь к хранилищу. Это также можно контролировать, устанавливая переменную среды GIT_DIR. Это может быть абсолютный или относительный путь к текущему рабочему каталогу.

Так что, если это кому-то поможет, вы все равно можете использовать git с пути и вносить изменения в хранилище «далеко от вас». Просто используйте:

git --git-dir=/path/to/repository GIT_COMMAND

или, чтобы запустить его от имени другого пользователя, сделайте что-то вроде:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository GIT_COMMAND

Также со справочной страницы git-init :

Если установлена ​​переменная среды $ GIT_DIR, то она указывает путь, который следует использовать вместо ./.git для базы репозитория.

Итак, если вы хотите запустить репозиторий из обычной папки .git, вам нужно будет указать его вместе с --git-dirопцией. например:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository/.git init

После инициализации репозитория /path/to/repo/.gitвсе последующие команды должны иметь опцию --work-tree=<path>, как описано на справочной странице git:

--work дерево =

Установите путь к рабочему дереву. Это может быть абсолютный путь или путь относительно текущего рабочего каталога. Это также можно контролировать, задав переменную среды GIT_WORK_TREE и конфигурационную переменную core.worktree (для более подробного обсуждения см. Core.worktree в git-config (1)).

Итак, правильная команда для запуска git от имени другого пользователя и инициализации нового репозитория:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository/.git init
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' add /path/to/repository/*
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' commit -m 'MESSAGE'
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' remote add origin user@domain.com:path
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' push -u origin master
dmmd
источник
Если вы используете sudo в интерактивном режиме, а не через скрипт, вы можете просто сделать это sudo -iили sudo suполучить интерактивную корневую оболочку.
Багстер
Я не могу представить, почему ( cd subdir && sudo -u USER /usr/bin/git init )бы не работать.
Скотт
Что если у меня нет разрешения на доступ subdir?
dmmd
13

Не совсем то, что вы спрашиваете (у вас есть реальные ответы выше с подоболочки), но посмотрите на pushdиpopd

Рич Гомолка
источник
Я попытался с помощью cd && для maven, и это не сработало, но pushd и popd отлично справляются со своей задачей. Спасибо
Раду Тоадер
6

У вас есть несколько вариантов. Вы можете сгруппировать команды с помощью &&или ;. Нравится:

cd subdir && git init && cd ..

или же

cd subdir; git init; cd ..

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

Другой вариант - определить функцию и использовать ее, например:

function cdinit() {
    cd $1
    git init
    cd ..
}

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

cdinit subdir

И он автоматически попадет git initв этот каталог и переместится из него.

Вы также можете сделать более сложное решение, используя функцию, если у вас есть несколько каталогов и вы хотите git initих с помощью одной команды.

function cdinit() {
    for arg in $@
    do
        cd $arg
        git init
        cd ..
    done
}

Затем вы можете запустить это с:

cdinit subdir1 subdir2 subdir3

И он будет делать git initв subdir1, subdir2и subdir3.

Wuffers
источник
Благодарю. Я знал &&и ;, но надеялся на что-то более элегантное. Похоже, написание сценария - мой лучший вариант.
Тревор Бёрнхем
Исправление: Этот ответ в порядке, но ответ Мэта лучше для моих конкретных потребностей.
Тревор Бёрнхем
Как вы думаете, ваша cdinitфункция может быть обобщена для произвольных команд? Я пытался использовать только аргументы, но это не сработало.
Четан Бхасин
(1) Новая ;строка эквивалентна , так что трехстрочная функция эквивалентна cd $1; git init; cd ... (2) Вы должны процитировать переменные: "$1", "$@"и "$arg". Или вы можете сократить for arg in "$@"до for arg.
Скотт
5

В случае git(по крайней мере, в версии 2.7.0) вы можете использовать -Cопцию, которая заставляет git вести себя так, как если бы он был запущен в данном каталоге. Таким образом, ваше решение может выглядеть так:

> git -C subdir init
Initialized empty Git repository in /some/path/subdir/.git/

Цитирование документации:

Run as if git was started in <path> instead of the current working directory. When multiple -C options are given, each subsequent non-absolute -C
<path> is interpreted relative to the preceding -C <path>.

This option affects options that expect path name like --git-dir and --work-tree in that their interpretations of the path names would be made
relative to the working directory caused by the -C option. 
Mifeet
источник
2

Вы можете сгруппировать команды с &&, т.е.

cd subdir && git init && cd ../

Если вы не хотите никакой зависимости от кода завершения каждой команды, вы можете использовать; вместо этого, то есть:

cd subdir ; git init ; cd ../
багор
источник
1
Или с ;тем, чтобы они не зависели от кода возврата предыдущего.
slhck
(1) на  cd subdir && git init ; cd ..самом деле может иметь смысл. Если пользователь хочет выполнить git initкоманду в subdir, то он, вероятно, не хочет запускать команду в текущем каталоге; то есть, они не хотят запускать его, если (первый) cdне работает. (Хотя вполне возможно, что  cdнеудачи, потому что мы уже в subdir, но это угловой случай.)… (Продолжение)
Скотт
(Продолжение)… Однако, если пользователь хочет рассматривать блок из трех команд как единое целое, он может захотеть выполнить  cdрезервное копирование в начальный каталог, даже если git initкоманда не выполнена. ( Или они могут захотеть остаться в подкаталоге и диагностировать сбой команды.) (2) Вам не нужно включать /после  ...
Скотт
2

Вам нужно перейти в целевой каталог, если команда не имеет имени файла или параметра имени каталога.

Но вы можете написать bash-скрипт, который принимает целевой каталог и команду в качестве параметров. Для этого вы можете взглянуть на pushd и popd: http://ss64.com/bash/pushd.html

Я написал бы этот маленький сценарий для вас, но у меня нет коробки с Linux здесь :)

wullxz
источник
Только что увидел ответ от Марка Шимански. Вы можете просто реализовать второй параметр для команды (и переименовать команду), и у вас есть то, что вы хотите.
wullxz
1

Программы имеют разные способы обработки аргументов, поэтому некоторые из них будут иметь эквивалентный параметр -folder = name . Помимо этого, стандарт, даже на MS DOS, просто

$ program subdir

Иногда нужно

$ program subdir /

Программа откроет папку, будет работать с ней так же, как вы работаете с файлом, и после ее завершения вернете управление вашей оболочке, которая указывает на исходный стандартный каталог. Программы, которые обрабатываются таким образом, ДЕЙСТВИТЕЛЬНО имеют проблему с тем, что выходные данные ошибок (например, дампы ядра) идут в файл в текущем каталоге вашей оболочки (а не в подкаталоге )

Обходного пути нет, если в программе нет доступных переключателей команд для указания другого места. Некоторые программисты берут художественную лицензию между «директорией была вызвана программа » и «директивной программой было приказано работать ».

Vlueboy
источник