Est-ce un UUOC (utilisation inutile de cat) de rediriger un fichier vers un autre?

36

Si je veux que le contenu de file2corresponde à celui de file1, je pourrais évidemment simplement courir cp file1 file2.

Toutefois, si je veux tout conserver, à l' file2 exception du contenu (propriétaire, autorisations, attributs étendus, ACL, liens physiques, etc.), je ne voudrais pas courir cp. contenu de file1dans file2.

Il semble que ce qui suit le ferait:

< file1 > file2

Mais ça ne marche pas. file2est tronqué à rien et non écrit. cependant,

cat < file1 > file2

ça marche.

Cela m'a surpris que la première version ne fonctionne pas.

La deuxième version est-elle un UUOC? Y a-t-il un moyen de faire cela sans invoquer une commande, simplement en utilisant des redirections?

Note: Je suis conscient que UUOC est plus un point de pédant qu'un véritable anti-pattern.

* Comme tniles09 découvert , cp volonté dans le travail fait dans ce cas.

Wildcard
la source
3
Que < file1 > file2ce soit ce que vous voulez dépend du shell.
Michael Homer
13
Eh bien, il est une utilisation Inutile de <...
jwodder
2
Qu'est-ce qu'un anti-motif ?
mikeserv
6
@ jwodder - ce n'est pas vrai. surtout quand vous parlez d'une copie. Considérez ce qui se passe quand file1n'existe pas ou est illisible et que vous l'ouvrez < avant que la > sortie ne soit ouverte, puis considérez ce qui se passe lorsque vous autorisez catà essayer de l'ouvrir.
mikeserv
3
@JonathanLeffler Dans zsh, une commande vide avec redirections invoque cat(par défaut), exécutant essentiellement la deuxième commande. Voir la réponse de Stéphane Chazelas ci-dessous pour plus de détails qu’un commentaire.
Michael Homer

Réponses:

58

cat < file1 > file2n'est pas un UUOC. Classiquement, <et >faire des redirections qui correspondent à des duplications de descripteur de fichier au niveau du système. Les duplications de descripteurs de fichiers en elles-mêmes ne font rien (bon, les >redirections sont ouvertes avec O_TRUNC, donc pour être exact, les redirections en sortie tronquent le fichier en sortie). Ne laissez pas les < >symboles vous confondre. Les redirections ne déplacent pas de données - elles attribuent des descripteurs de fichier à d'autres descripteurs de fichier.

Dans ce cas, vous ouvrez file1et affectez ce descripteur de fichier au descripteur de fichier 0( <file1== 0<file1) file2et affectez ce descripteur de fichier au descripteur de fichier 1( >file2== 1>file2).

Maintenant que vous avez deux descripteurs de fichier, vous avez besoin d'un processus pour regrouper les données entre eux - et c'est ce à quoi ils catservent.

PSkocik
la source
11
Peut-être que c'est juste moi, mais ma partie préférée de cette réponse est votre utilisation du mot "pelle". :) Très clair, merci.
Wildcard
1
@Wildcard J'aurais préféré "pomper" que "pelle", mais ça reste un bon mot. +1
Mehrdad
pourquoi est-ce que pelle est un bon mot?
bubakazouba
1
On pelle une pile de terre, une pelle pleine à la fois, d’une pile à l’autre, les données étant copiées tampon par tampon. C'est une bonne analogie.
bsd
1
Dans votre première phrase, vous dites que les descripteurs de fichier sont en cours de duplication. Sont-ils dupliqués ou réaffectés (comme semblent l'indiquer votre deuxième paragraphe et le comportement de la fonction)?
Greg Bell
17

Ce n'est pas le cas, car, comme d'autres l'ont souligné, le comportement en question dépend du shell. Comme vous l'avez fait remarquer, c'est un peu pédant , peut-être même drôle. , genre de sujet.

Cependant, sur les systèmes GNU, votre prémisse a une autre solution disponible: cp --no-preserve=all file1 file2. Essayez ceci, je pense que cela satisfera la situation décrite (par exemple, modifier le contenu de file2tout en ne modifiant pas ses attributs).

Exemple :

$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 16 Dec 16 12:21 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Lookout, world!
    Hello, world!
$ cp --no-preserve=all fred fezzik 
$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 14 Dec 16 12:22 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Hello, world!
    Hello, world!

UPDATE En fait, je viens de remarquer que mon système lui- cpmême semble conserver les attributs sauf si -aou -psont spécifiés. J'utilise bash shell et GNU coreutils. Je suppose que vous apprenez quelque chose de nouveau chaque jour ...


Résultats de test (par Wildcard) incluant un lien physique et différentes autorisations:

$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 file2
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the original contents of file2
$ cp file1 file2
$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 file2
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the contents of file1
$ 
tiles
la source
Agréable. J'ai effectué mon propre test, y compris un lien physique et différentes autorisations, et il semble que vous ayez raison.
Wildcard
Ajouté mes résultats de test; J'espère que ça ne vous dérange pas. :) Je n'ai pas testé les ACL ni les attributs étendus, mais étant donné que le numéro d'inode est conservé, je suis sûr à 99% qu'ils le seraient également.
Wildcard
Nice ... ça ne me dérange pas du tout. :-)
novembre
13

Dans zsh, le shell où < file1 > file2fonctionne, le shell appelle cat.

Pour une ligne de commande composée uniquement de redirections et pas de commande ni d'assignations, zshinvoque $NULLCMD( catpar défaut) à moins que la seule redirection ne soit une redirection, <auquel cas $READNULLCMD( pagerpar défaut) est appelée à la place. (à moins que ce ne zshsoit dans shou l' cshémulation, auquel cas il se comporte comme les coquilles qu'il émule).

Alors:

< file1 > file2

est en fait le même que

cat < file1 > file2

et

< file1

est le même que

pager < file1
Stéphane Chazelas
la source
Pour mémoire, cette syntaxe ne fonctionne pas pour ksh93
fpmurphy
8
< from > to

ne fonctionne pas car il n'y a pas de commande là-bas; pas de processus. Le shell ouvre / crée les fichiers et organise les redirections (ce qui signifie que les descripteurs de fichiers référençant ces fichiers sont définis comme 0 et 1: entrée standard et sortie standard). Mais il n’ya rien à faire là-bas, exécuter une boucle pour lire à partir de l’entrée standard et écrire sur la sortie standard.

zsheffectue ce travail en substituant une commande configurable par l'utilisateur dans ce cas de "commande NULL". La commande n'est pas visible dans la ligne de commande, mais elle est toujours là. Un processus est créé pour cela et il fonctionne de la même manière. NULLCMDest catpar défaut, donc signifie en < from > toréalité dans cat < from > tozsh , sauf si NULLCMDest défini sur autre chose; c'est une commande "chat implicite".

Une "utilisation inutile de chat" se produit lorsque cat est utilisé comme intermédiaire pour lire un fichier et transmettre les données à un autre processus, dont le descripteur de fichier pourrait simplement être connecté au fichier d'origine.

Si catest amovible de la situation, de sorte que les commandes restantes peuvent toujours effectuer la même tâche, il est inutile. Si ce n'est pas amovible, alors ce n'est pas inutile.

# useless, removable:
$ cat archive.tar | tar tf -    #  -->  tar tf archive.tar

# not removable (in POSIX shell):
$ cat > file
abc
[Ctrl-D]

# likewise:
STRING=$(cat file)

A catqui est replacable est pas la même chose. Par exemple, au lieu de, cat > filenous pouvons utiliser vi filepour créer le fichier. Cela ne compte pas comme suppression decat , tout en utilisant ce qui reste pour accomplir la même tâche.

Si catest le seul commande du pipeline, elle ne peut évidemment pas être supprimée. aucune réorganisation de ce qui reste ne fera le travail équivalent.

Certains scripteurs de shell utilisent catparce qu’ils pensent que cela leur permet de déplacer l’opérande d’entrée plus près du côté gauche de la ligne de commande. Cependant, les redirections peuvent être n'importe où dans la ligne de commande:

# If you're so inclined:
# move source archive operand to the left without cat:
$ < archive.tar tar xf - > listing
Kaz
la source
Au fait, vous ne devez pas utiliser f -de goudron. tar xf -est juste tar x.
dnt
@mikeserv Où dit-on que cela catest impliqué dans la création du fichier? La réponse dit clairement que la coquille fait cela. De quel problème > fileparlez-vous? Je l’utilise souvent seul pour tronquer un fichier existant à une longueur nulle ou pour s’assurer qu’il existe. Cette question concerne pourquoi < from > tone fonctionne pas comme cat < from > toUUoC et non pas "donnez-moi s'il vous plaît des raisons pour lesquelles ce catn'est pas un bon substitut pour cp".
Kaz
1
@dnt, tarest l' archiveur de bandes . De nombreuses tarimplémentations fonctionnent toujours avec le premier lecteur de bande par défaut.
Stéphane Chazelas
1

< file1 > file2 Cela semble dépendre du shell, sur zsh cela fonctionne, mais pas sur bash.

edit: déclaration fausse supprimée

goût
la source
cp -apréserve les attributs de fichier1 et remplace les attributs de fichier2. Opposé du comportement souhaité. De plus, je ne peux même pas dire en consultant la page de manuel ce qui se passera avec les liens physiques, mais je pense qu’il est prudent de dire que les liens physiques de file2 ne seront pas conservés.
Wildcard
Vous avez raison, je n'ai pas lu la question assez attentivement.
goût
1

En plus de toutes les bonnes réponses, vous pouvez éviter un UUOC en simulant un cat:

awk 1 file1 > file2   # For line-oriented text, not binaries.
dd if=file1 of=file2  # Works for binary files, too.
# many more geeky ways.

Ces commandes ne copient pas les métadonnées du fichier, en clair cp ferait .

Jens
la source
C'est vrai, mais il convient de mentionner qu'ils n'ont aucun avantage et que leurs inconvénients (performances, fiabilité) cat. Ici, vous avez besoin d’une commande pour déplacer les données entre les deux descripteurs de fichier et catc’est l’un des meilleurs pour cela. Voyez aussi pvce qui pourrait être utilisé splice()sous Linux pour fifos (bien que ce ne soit pas fadvise(POSIX_FADV_SEQUENTIAL)comme GNU cat).
Stéphane Chazelas
La ddcommande pour les fichiers binaires semble bonne ... ou catfonctionnerait-elle aussi bien pour les fichiers binaires?
Wildcard
@Wildcard catfonctionne également pour les fichiers binaires (Unix ne distingue généralement pas; cependant, certains outils fonctionnent spécifiquement ligne par ligne, tels que awk, grep, wc, ... POSIX définit également une longueur minimale de ligne, donc en théorie un outil orienté ligne pourrait refuser de traiter des lignes excessivement grandes.)
Jens
2
@ StéphaneChazelas Cette réponse était également destinée à être ironique. On dirait que, malgré la saison, certaines personnes sont allergiques au plaisir (ce n’est pas destiné à vous; j’apprécie votre expertise en matière de shell et le travail effectué par les normes Opengroup).
Jens
sed '' < file1 > file2;-)
Digital Trauma
0

Si cela fonctionne, ne le répare pas.

j'utiliserais

cat < file1 > file2

et ne pas transpirer le PC de la sémantique.

krazykyngekorny
la source