Comment échanger des colonnes dans un tel fichier?


7

J'ai un fichier texte, chaque ligne est stockée comme ceci:

"Video or movie"    "parent"    "Media or entertainment"    "1" "1" "1" "0" "0"

Je veux échanger les colonnes 3 avec 2, à savoir

"Video or movie"   "Media or entertainment"  "parent"   "1" "1" "1" "0" "0"

Comment le faire sous Linux dans un script shell ou tout autre langage de script? J'ai juste besoin d'un moyen le plus simple et le plus rapide de le faire.


Votre dernière phrase est incomplète.
Faheem Mitha

1
Vous pouvez essayer R
Faheem Mitha

Un processeur CSV, avec des espaces comme séparateur de colonnes?
Gilles

@FaheemMitha Pouvez-vous nous donner un exemple ici comment vous feriez avec R?
Léo Léopold Hertz 준영

Réponses:


10

Cela peut être fait en awkutilisant "comme séparateur de champs. Mais, pour ce faire, vous devez vous rappeler que la chaîne $1est vide, qu'elle $2contient la première chaîne, qu'il $3y a un espace entre les chaînes, qu'elle $4est la deuxième chaîne, etc. assez de $ns. Compte tenu de ces éléments, les éléments suivants devraient fonctionner:

awk 'BEGIN{OFS=FS="\""} {tmp=$4;$4=$6;$6=tmp;print}' input_file >output_file

+1 pour une utilisation aussi élégante de awk.
Stephen Quan

1

Voici un rapide et sale sedqui le fait:

sed -e 's/^\("[^"]*"\) *\("[^"]*"\) *\("[^"]*"\)/\1 \3 \2/'

Mais échouera pour les champs avec des guillemets doubles dans leurs valeurs, etc.

Un exemple:

echo \"a\" \"b\" \"c d d d\" \"e\" | sed -e 's/^\("[^"]*"\) *\("[^"]*"\)  *\("[^"]*"\)/\1 \3 \2/'

Mais je suis sûr que quelqu'un sera capable de vous montrer une awkligne plus simple et meilleure.


cela n'a pas fonctionné pour cette ligne: "ennuyé de la mélodie principale" "mélodie" "parcelle principale" "1" "1" "1" "0" "1.0" "0" "0"
utilisateur11498

@frankmoss je me lasse de l'intrigue principale "" principale "" mélodie "" 1 "', n'est-ce pas ce que vous voulez? Qu'est-ce que vous obtenez
Kevin

1
Cela pourrait être un problème d’espace, essayez de remplacer «*» par [ \t]*.
Kevin

@ Kevin je reçois la ligne d'origine. J'ai remplacé par [\ t] * mais aucun changement dans le résultat. Comment est votre sed?
user11498

@frankmoss j'ai mis echo '"bored of the main" "melody" "main plot" "1"' | sed -e 's/^\("[^"]*"\) *\("[^"]*"\) *\("[^"]*"\)/\1 \3 \2/', et est sorti "bored of the main" "main plot" "melody" "1"- copié et collé, pas de fautes de frappe dans non plus. GNU sed version 4.2.1
Kevin

1

J'irais avec:

sed 's/"\(.*\)"/\1/' |
    awk 'BEGIN{FS="\" +\"";OFS="\" \""}{t=$3;$3=$2;$2=t;print}' |
    sed 's/.*/"&"/'

Les deux sedscripts gèrent les guillemets avant et arrière (car ils ne sont pas des délimiteurs et interfèrent). La BEGINclause gère la séparation des champs. Le t=$3;$3=$2;$2=test un idiome standard de champs d'échange, puis le champ entier est imprimé (avec OFS comme séparateur de champs).


ça ne marche pas. J'envoie un fichier d'une seule ligne et le même fichier est imprimé.
user11498

En utilisant votre entrée ( "Video or movie" "parent" "Media or entertainment" "1" "1" "1" "0" "0") et en copiant ce qui précède (sans utiliser mes incarnations précédentes), je reçois "Video or movie" "Media or entertainment" "parent" "1" "1" "1" "0" "0".
Arcege

1

Cette méthode est en réalité identique à celle de Kevin awk . Je l'ai incluse ici simplement à titre de comparaison entre bash et awk .

IFS=\";   # IFS sets up the split-at array delimiter
cat file |
while IFS= read -r line ;do              # Disable IFS for each `read' 
    A=($line)                            # split into array elements
    t="${A[5]}";A[5]="${A[3]}";A[3]="$t" # swap "columns" 2 and 3
    for ((i=1;i<$((${#A[@]}));i++)) ;do
       printf '"%s' "${A[$i]}"           # print each element with a lead "
    done; echo '"'                       # add the final "
done

0

Qu'en est-il quelque chose comme ceci:

awk '{print $1, $3, $2, $4, $5, $6, $7, $8}' file > newfile

@ramonovsky awk n'aime pas la combinaison de guillemets et d'espace ... c'est-à-dire que cela ne fonctionne pas
user11498

Vous pouvez spécifier '' en tant que -F (séparateur de champ)
ramonovski

Et qu'est-ce que -F' ' do? The problem is that this will print (e.g. in the first case) "film vidéo" ou "parent" ... `, clairement pas ce que veut Frank.
Kevin

0

J'ai deux solutions.

RÉPONSE UNE: Utilisez sed3 fois pour faire ce qui suit:

  1. commencez par la ligne d'origine: 12345678
  2. dupliquer les deux premiers paramètres: 1212345678
  3. hacher le 4ème paramètre: 121345678
  4. hachez le 1er paramètre: 21345678

Voici la commande résultante utilisant sed(Utilisation de XXX et YYY comme aide pour trouver et supprimer les premier et quatrième paramètres):

sed 's/["][^"]*["][^"]*["][^"]*["]/XXX& & YYY/' data.txt | sed 's/["][^"]*["] YYY//' | sed 's/XXX["][^"]*["]//'

RÉPONSE DEUX: Convertissez les données en script et exécutez-le!

  • convertir le fichier data.txt en fichier data.sh en insérant une commande (disons flipcol.sh)
  • lancer le data.sh

Implémentez flipcol.sh en tant que:

echo '"'$2'"' '"'$1'"' '"'$3'"' '"'$4'"' '"'$5'"' '"'$6'"' '"'$7'"' '"'$8'"'

Ensuite, exécutez la commande suivante sur votre data.txt créant un script shell:

sed 's!^!./flipcol.sh !' < data.txt > data.sh

Puis exécutez le script shell nouvellement créé

./data.sh

0

Hypothèses sur les lignes du fichier: elles commencent par un guillemet double et le champ et les délimiteurs ne contiennent pas de guillemets. Ensuite, cette expression sed fonctionnera:

sed 's@^\("[^"]*"[^"]*"\)\([^"]*\)\("[^"]*"\)\([^"]*\)@\1\4\3\2@'

Si cette expression est suivie du nom du fichier texte, elle donnera la sortie souhaitée, qui peut être insérée dans un nouveau fichier en suivant le nom du fichier texte par le redirecteur de sortie >et le nom du nouveau fichier souhaité, comme suit :

sed 's@^\("[^"]*"[^"]*"\)\([^"]*\)\("[^"]*"\)\([^"]*\)@\1\4\3\2@' textfile > newfile
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.