Para una tarea, se me pide que escriba inteligentemente una función bash que tenga la misma funcionalidad básica que la función cp
(copiar). Solo tiene que copiar un archivo a otro, por lo que no se copian varios archivos a un nuevo directorio.
Como soy nuevo en el lenguaje bash, no puedo entender por qué mi programa no funciona. La función original pide sobrescribir un archivo si ya está presente, así que intenté implementarlo. Falla.
Parece que el archivo falla en varias líneas, pero lo más importante es que comprueba si el archivo a copiar ya existe ( [-e "$2"]
). Aun así, todavía muestra el mensaje que se supone que se activará si se cumple esa condición (El nombre del archivo ...).
¿Alguien podría ayudarme a arreglar este archivo, posiblemente proporcionando alguna información útil en mi comprensión básica del lenguaje? El código es el siguiente.
#!/bin/sh
echo "file variable: $2"
if [-e file]&> /dev/null
then
echo "The file name already exists, want to overwrite? (yes/no)"
read | tr "[A-Z]" "[a-z]"
if [$REPLY -eq "yes"] ; then
rm "$2"
echo $2 > "$2"
exit 0
else
exit 1
fi
else
cat $1 | $2
exit 0
fi
[ ... ] &> /dev/null
. Las pruebas admitidas por[ ... ]
son siempre silenciosas, solo se produce un resultado de sintaxis. Silenciar tal prueba es simplemente cavar tu propia tumba.cat $1 | $2
"canaliza" la salida del primer comando al comando en su segunda variable. Pero ese es un nombre de archivo, no el nombre de un comando para ejecutar.[
comando en suif
.|
y redirigir a un archivo con>
. Estos son problemas básicos de sintaxis que ya deberían haberse cubierto en su clase.Respuestas:
La
cp
utilidad sobrescribirá felizmente el archivo de destino si ese archivo ya existe, sin preguntar al usuario.Una función que implementa la
cp
capacidad básica , sin usarcp
seríaSi desea solicitar al usuario antes de sobrescribir el destino (tenga en cuenta que puede que no sea conveniente hacerlo si un shell no interactivo llama a la función):
Los mensajes de diagnóstico deben ir al flujo de error estándar. Esto es con lo que hago
printf ... >&2
.Tenga en cuenta que realmente no necesitamos
rm
el archivo de destino ya que la redirección lo truncará. Si nosotros nos queremosrm
en primer lugar, a continuación, usted tendría que comprobar si se trata de un directorio, y si lo es, poner el archivo de destino dentro de ese directorio, tal y comocp
haría. Esto está haciendo eso, pero aún sin explícitorm
:También puede asegurarse de que la fuente realmente exista, que es algo que
cp
sí lo hace (cat
también lo hace, por lo que puede omitirse por completo, por supuesto, pero hacerlo crearía un archivo de destino vacío):Esta función no utiliza "bashismos" y debería funcionar en todos los
sh
shells.Con un poco más de ajustes para admitir múltiples archivos fuente y una
-i
marca que activa la solicitud interactiva al sobrescribir un archivo existente:Su código tiene mal
if [ ... ]
espaciado (necesita espacio antes y después[
, y antes]
). Tampoco debe intentar redirigir la prueba/dev/null
ya que la prueba en sí no tiene salida. Además, la primera prueba debe usar el parámetro posicional$2
, no la cadenafile
.Usando
case ... esac
como lo hice, evitas tener que poner en minúscula / mayúscula la respuesta del usuario que usatr
. Enbash
, si hubiera querido hacer esto de todos modos, una forma más económica de hacerlo hubiera sido usarREPLY="${REPLY^^}"
(para mayúsculas) oREPLY="${REPLY,,}"
(para minúsculas).Si el usuario dice "sí", con su código, la función coloca el nombre de archivo del archivo de destino en el archivo de destino. Esto no es una copia del archivo fuente. Debe pasar al bit de copia real de la función.
El bit de copia es algo que ha implementado usando una tubería. Una tubería se utiliza para pasar datos desde la salida de un comando a la entrada de otro comando. Esto no es algo que debamos hacer aquí. Simplemente invoque
cat
en el archivo fuente y redirija su salida al archivo de destino.Lo mismo está mal con tu llamado de
tr
antes.read
establecerá el valor de una variable, pero no produce salida, por lo que canalizarread
cualquier cosa no tiene sentido.No se necesita una salida explícita a menos que el usuario diga "no" (o la función se encuentra con alguna condición de error como en bits de mi código, pero como es una función que uso en
return
lugar deexit
).Además, dijiste "función", pero tu implementación es un script.
Eche un vistazo a https://www.shellcheck.net/ , es una buena herramienta para identificar partes problemáticas de scripts de shell.
Usar
cat
es solo una forma de copiar el contenido de un archivo. Otras formas incluyendd if="$1" of="$2" 2>/dev/null
sed "" "$1" >"2"
orawk '1' "$1" >"$2"
otr '.' '.' <"$1" >"$2"
etc.El truco es hacer que la función copie los metadatos (propiedad y permisos) del origen al destino.
Otra cosa a tener en cuenta es que la función que he escrito se comportará de manera bastante diferente a
cp
si el objetivo es algo así como,/dev/tty
por ejemplo (un archivo no regular).fuente
cat "$1" >"$2"
funcionará 2 veces: el shell ve por primera vez> "$2"
, lo que significa: crear-o-clobber (= vacío) el archivo "$ 2", y redirigir la salida estándar del comando a ese archivo. Luego, inicia el comando:cat "$1"
y redirige su stdout a un archivo"$2"
(ya vaciado o creado vacío).-ef
comparaciones de archivos ylocal -a
.-ef
toma avisos si los dos archivos son iguales (también se encarga de los enlaces) ylocal -a
crea una variable de matriz local.