Unix: crea una ruta de carpetas y archivos

125

Sé que puede hacer mkdirpara crear un directorio y touchcrear un archivo, pero ¿no hay forma de hacer ambas operaciones de una vez?

es decir, si quiero hacer lo siguiente cuando la carpeta otherno existe:

cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

Error:

cp: cannot create regular file `/my/other/path/here/cpedthing.txt': No such file or directory

¿Alguien ha ideado una función como solución para esto?

toop
fuente
1
Encontré esto: stackoverflow.com/questions/1529946/…
Bjørne Malmanger
Si es esencial que la creación del archivo y su directorio sea atómica, deberá escribir un sistema de archivos que ofrezca esta operación. No es posible con los sistemas de archivos estándar de Linux.
Peter G.
@toop Entiendo que esta pregunta tiene ahora un año y medio de antigüedad, pero recientemente se fusionaron varias respuestas. Si necesita este tipo de cosas muy a menudo, puede encontrar mi respuesta útil. (Yo diría que es más útil que la respuesta aceptada, pero no estoy rogando por representante aquí :-))
Jonathon Reinhart
Encontrado esto: stackoverflow.com/a/8722721/29182
Ziggy
@tdammers Q: "¿Cómo hago X?" R: "He aquí cómo hacer Y"
Henry Henrinson

Respuestas:

139

Use &&para combinar dos comandos en una línea de shell:

COMMAND1 && COMMAND2
mkdir -p /my/other/path/here/ && touch /my/other/path/here/cpedthing.txt

Nota: Anteriormente recomendé el uso de ;para separar los dos comandos, pero como lo señaló @trysis, probablemente sea mejor usarlo &&en la mayoría de las situaciones porque, en caso de que COMMAND1falle COMMAND2, tampoco se ejecutará. (De lo contrario, esto podría conducir a problemas que podría no haber estado esperando).

evotopid
fuente
16
Probablemente sea mejor usar &&, como con ;, si el primer comando falla, el segundo comando seguirá ejecutándose (y también fallará, ya que ese directorio aún no existe). Con &&si el primer comando falla, el segundo comando no se ejecuta.
trysis
12
Un atajo para el ejemplo de código puede ser: mkdir -p /my/other/path/here && touch $_/cpredthing.txt. El se $_expande esencialmente al "último argumento en el último comando ejecutado".
Sgnl
44
@Sgnl la tuya es posiblemente la respuesta correcta. No solo es más limpio, es menos propenso a errores.
Choylton B. Higginbottom
3
Realmente deseo touchtomar un -pparámetro como lo mkdirhace.
Ben174
86

Primero debe crear todos los directorios principales.

FILE=./base/data/sounds/effects/camera_click.ogg

mkdir -p "$(dirname "$FILE")" && touch "$FILE"

Si quieres ser creativo, puedes hacer una función :

mktouch() {
    if [ $# -lt 1 ]; then
        echo "Missing argument";
        return 1;
    fi

    for f in "$@"; do
        mkdir -p -- "$(dirname -- "$f")"
        touch -- "$f"
    done
}

Y luego úsalo como cualquier otro comando:

mktouch ./base/data/sounds/effects/camera_click.ogg ./some/other/file
Jonathon Reinhart
fuente
Lo cambié para usar &&- ¿Cómo es eso?
Jonathon Reinhart
1
@ BЈовић . (Intento probar cosas antes de publicarlas.)
Jonathon Reinhart
2
@JonathonReinhart ¿Estás seguro de que lo has probado? Lo intenté supertouch "/tmp/abc/def ghi/jkl mno.txt"y falló. Todos los comandos dirname, mkdiry touchdieron erros.
devnull
2
@devnull Aparentemente no lo suficientemente bien. Malditos sean tus espacios :-) Corregido.
Jonathon Reinhart
1
@JeremyIglehart Me gusta. mktouches.
Jonathon Reinhart
24

Hazlo con / usr / bin / install:

install -D /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

cuando no tienes un archivo fuente:

install -D <(echo 1) /my/other/path/here/cpedthing.txt
Ivan se zambulle
fuente
1
Me sorprende que esta herramienta de gemas se ignore en gran medida (instalación de hombre) mostrará que instalar es una mejor solución.
SC-SL
1
También puedes usar -D /dev/null, lo cual es más fácil para mí recordar.
wisbucky
14

Esto es lo que haría:

mkdir -p /my/other/path/here && touch $_/cpredthing.txt

Aquí, $_es una variable que representa el último argumento del comando anterior que ejecutamos en línea.

Como siempre, si desea ver cuál podría ser el resultado, puede probarlo utilizando el echocomando, de la siguiente manera:

echo mkdir -p /code/temp/other/path/here && echo touch $_/cpredthing.txt

Que sale como:

mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt

Como beneficio adicional, puede escribir varios archivos a la vez utilizando la expansión de llaves, por ejemplo:

mkdir -p /code/temp/other/path/here &&
touch $_/{cpredthing.txt,anotherfile,somescript.sh}

De nuevo, totalmente comprobable con echo:

mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt /code/temp/other/path/here/anotherfile /code/temp/other/path/here/somescript.sh
Sgnl
fuente
13
#!/bin/sh
for f in "$@"; do mkdir -p "$(dirname "$f")"; done
touch "$@"
Javier
fuente
1
Un usuario anónimo sugirió (a través de la edición) usar dirname -z "$@" | xargs -0 mkdir -pen su lugar, por el bien del rendimiento. (¡Deja de acechar y únete, por el amor de Dios! ;-)
jpaugh el
11

puedes hacerlo en dos pasos:

mkdir -p /my/other/path/here/
touch /my/other/path/here/cpedthing.txt
Jörg Beyer
fuente
2
if [ ! -d /my/other ]
then
   mkdir /my/other/path/here
   cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
fi
Barun Parichha
fuente
2

como vi y probé en un foro de Unix, esto resuelve el problema

ptouch() {
    for p in "$@"; do
        _dir="$(dirname -- "$p")"
        [ -d "$_dir" ] || mkdir -p -- "$_dir"
    touch -- "$p"
    done
}
Fabian Rios
fuente
1

sin necesidad de if thendeclaraciones ... puede hacerlo en una sola línea;

mkdir -p /my/other/path/here;cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

- o en dos líneas -

mkdir -p /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

- el -perror de prevención regresa si el directorio ya existe (que es lo que vine a buscar aquí :))

tediffer3rd
fuente
1

En el caso especial (pero no infrecuente) en el que intenta recrear la misma jerarquía de directorios, cp --parentspuede ser útil.

Por ejemplo, si /my/longcontiene los archivos de origen y my/otherya existe, puede hacer esto:

cd /my/long
cp --parents path/here/thing.txt /my/other
Todd Owen
fuente
-4

si quieres simple con solo 1 fragmento de parámetro:

rm -rf /abs/path/to/file;  #prevent cases when old file was a folder
mkdir -p /abs/path/to/file; #make it fist as a dir
rm -rf /abs/path/to/file; #remove the leaf of the dir preserving parents 
touch /abs/path/to/file; #create the actual file
desconocido
fuente
3
Si simplemente quiere deshacerse del trabajo de las últimas horas †, agregue con gratitud algunos rm -rfcomandos. - † Es decir, horas , suponiendo que se adhiera a una política sensata de control de versiones / copia de seguridad ... de lo contrario, podrían pasar meses.
Leftaroundabout
1
rm -rfnunca debe sugerirse sin una explicación y advertencias adecuadas.
Mausy5043