Ejecuta un comando desde otro directorio en bash

119

Di que estoy haciendo esto:

cd subdir
git init
cd ../

¿Hay alguna manera de hacer esto con un solo comando, o tal vez dos, en lugar de tener que entrar y salir de un directorio para ejecutar un comando allí?

(No busco una solución específica de git; eso es solo un ejemplo).

Trevor Burnham
fuente

Respuestas:

202

Esta suele ser la mejor manera:

( cd dir ; git init )

o

( cd dir && git init )

Es bastante corto y fácil de escribir. Inicia un sub-shell, por lo que no puede modificar su entorno a partir de eso, pero eso no parece ser un problema aquí.

Estera
fuente
1
Y si necesita establecer una variable de entorno o dos, simplemente agréguela como otro comando al principio.
Hippo
1
@CraigMcQueen: en realidad no. $?contendrá el código de salida del último comando que se ejecutó en la subshell. Si usa la &&variante (que normalmente debería), obtendrá el código de salida del primer comando que falló (o 0 si todo salió bien).
Mat
Creo que los paréntesis son opcionales
Francis.Beauchamp
10
@ Francis.Beauchamp: si omites los parens, estarás en el subdirectorio después del comando en lugar de volver a donde comenzaste.
Mat
¿Cómo se hace esto en Windows?
Karl Morrison
22

Estaba buscando una manera de ejecutar el comando git desde una ruta y hacer cambios en el repositorio en una ruta diferente. Así que terminé en esta pregunta aquí.

Pero para mis necesidades específicas, ni la respuesta aceptada ni ninguna de las otras me ayudaron.

Necesitaba ejecutar comandos git usando sudo -u USER /usr/bin/git(otro usuario lo ejecuta). Y como sabrán, sudo no me permite ejecutar el cdcomando, por lo que no puedo estar en el directorio del repositorio.

Entonces, fui a la página de manual de git . Y entre las opciones, vi el --git-dir=<path>:

--git-dir =

Establecer la ruta al repositorio. Esto también se puede controlar configurando la variable de entorno GIT_DIR. Puede ser una ruta absoluta o relativa al directorio de trabajo actual.

Entonces, si ayuda a alguien, aún puede usar git desde una ruta y hacer cambios en un repositorio "lejos de usted". Solo usa:

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

o, para ejecutarlo como otro usuario, haga algo como:

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

También de la página de manual de git-init :

Si se establece la variable de entorno $ GIT_DIR, especifica una ruta para usar en lugar de ./.git para la base del repositorio.

Por lo tanto, si desea iniciar el repositorio en la carpeta .git habitual, deberá especificarlo junto con la --git-diropción. p.ej:

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

Después de inicializar el repositorio /path/to/repo/.git, todos los comandos adicionales deben tener la opción --work-tree=<path>, como se describe en la página de manual de git:

--work-tree =

Establecer la ruta al árbol de trabajo. Puede ser una ruta absoluta o una ruta relativa al directorio de trabajo actual. Esto también se puede controlar configurando la variable de entorno GIT_WORK_TREE y la variable de configuración core.worktree (vea core.worktree en git-config (1) para una discusión más detallada).

Entonces, el comando correcto para ejecutar git como otro usuario e inicializar un nuevo repositorio es:

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
fuente
Si está utilizando sudo de forma interactiva y no a través de un script, simplemente puede hacer sudo -io sudo suobtener un shell raíz interactivo.
Bugster
No puedo imaginar por ( cd subdir && sudo -u USER /usr/bin/git init )qué no funcionaría.
Scott
¿Qué pasa si no tengo permiso para acceder subdir?
dmmd
13

No es exactamente lo que estás preguntando (tienes respuestas reales arriba con la subshell) pero mira pushdypopd

Rich Homolka
fuente
Intenté con cd && para maven y no funcionó, pero pushd y popd funcionan perfectamente. Gracias
Radu Toader
6

Tienes pocas opciones. Puede agrupar los comandos con &&o ;. Me gusta esto:

cd subdir && git init && cd ..

o

cd subdir; git init; cd ..

La diferencia entre estos es que en el primer ejemplo, si uno de los comandos falla, no ejecutará el resto de ellos. En el segundo ejemplo, todos los comandos se ejecutarán sin importar qué.

Otra opción sería definir una función y usarla, por ejemplo:

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

Entonces puedes ejecutar el comando:

cdinit subdir

Y estará automáticamente git initen ese directorio y saldrá de él.

También podría hacer una solución más compleja usando una función si tiene un montón de directorios y los quiere git initcon un solo comando.

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

Luego puede ejecutar esto con:

cdinit subdir1 subdir2 subdir3

Y lo hará git initen subdir1, subdir2, y subdir3.

Wuffers
fuente
Gracias. Era consciente de &&y ;, pero esperaba algo más elegante. Parece que escribir un guión es mi mejor opción.
Trevor Burnham
Corrección: esta respuesta está bien, pero la respuesta de Mat es mejor para mis necesidades particulares.
Trevor Burnham
¿Crees que tu cdinitfunción puede generalizarse para comandos arbitrarios? Intenté usar solo los argumentos, pero eso no funcionó.
Chetan Bhasin
(1) Newline es equivalente a ;, por lo que la función de tres líneas es equivalente a cd $1; git init; cd ... (2) Usted debe citar sus variables: "$1", "$@"y "$arg". O puede abreviar for arg in "$@"a for arg.
Scott
5

En el caso de git(al menos en la versión 2.7.0), puede aprovechar la -Copción que hace que git se comporte como si se iniciara en el directorio dado. Entonces su solución puede verse así:

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

Citando la documentación:

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
fuente
2

Puede agrupar los comandos con &&, es decir

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

Si no desea ninguna dependencia en el código de salida de cada comando, puede usar; en cambio, es decir:

cd subdir ; git init ; cd ../
Garfio
fuente
1
O con ;para que no dependan del código de retorno del anterior.
slhck
(1) en  cd subdir && git init ; cd ..realidad puede tener más sentido. Si el usuario desea ejecutar el git initcomando en el subdir, probablemente no quiera ejecutar el comando en el directorio actual; es decir, no quieren ejecutarlo si el (primero) cdfalla. (Aunque es posible que la  cdfalla porque estamos ya en el subdir, pero eso es un caso límite.) ... (Continuación)
de Scott
(Cont.) ... Sin embargo, si el usuario desea tratar el bloque de tres comandos como una unidad, es posible que desee realizar una  cdcopia de seguridad en el directorio inicial incluso si el git initcomando falla. ( O bien, es posible que quieran permanecer en el subdirectorio y diagnosticar el fallo del comando). (2) No es necesario incluir el /after  ...
Scott
2

Debe saltar a su directorio de destino si el comando no tiene un nombre de archivo o parámetro de nombre de directorio.

Pero puede escribir un script bash que tome el directorio de destino y el comando como parámetros. Para esto, puede echar un vistazo a pushd y popd: http://ss64.com/bash/pushd.html

Escribiría ese pequeño script para ti, pero no tengo un cuadro de Linux aquí :)

wullxz
fuente
Acabo de ver la respuesta de Mark Szymanski. Simplemente podría implementar un segundo parámetro para un comando (y cambiar el nombre del comando) y obtendrá lo que desea.
wullxz
1

Los programas tienen diferentes formas de manejar argumentos, por lo que algunos tendrán algún equivalente de la opción -folder = name . Más allá de esa excepción, el estándar, incluso en MS DOS, es simplemente

$ program subdir

A veces necesitas

$ program subdir /

El programa abrirá la carpeta, trabajará con ella de la misma manera que trabaja con un archivo y, una vez terminado, devolverá el control a su shell, que apunta a su directorio estándar original. Los programas manejados de esta manera TIENEN el problema de que las salidas de error (como los volcados de núcleo) van a un archivo en el directorio actual de su shell (en lugar de subdireccionar ).

No hay una solución alternativa a menos que el programa tenga interruptores de comando disponibles para especificar un lugar diferente. Algunos programadores obtienen una licencia artística entre "se llamó al programa de directorio " y "se le dijo al programa de directorio que trabajara ".

Vlueboy
fuente