2015-02-25 17 views
7

Ho 10 file di testo e voglio il file paste con la sua coppia, tale che ho 5 file totali.Looping di coppie di valori in bash

Ho provato il seguente:

for i in 4_1 5_1 6_1 7_1 8_1 
do 
for j in 4_2 5_2 6_2 7_2 8_2 
do 
paste ${i}.txt ${j}.txt > ${i}.${j}.txt 
done 
done 

Tuttavia, questo codice combina ogni possibile combinazione invece di combinare gli accoppiamenti.

quindi vorrei lima 4_1.txt per essere accoppiato con 4_2.txt, 5_1.txt con 5_2.txt, ecc

+0

È necessario eseguire il looping sui prefissi e sui suffissi non sui nomi di file completi. –

+0

Il comportamento che si ottiene è uguale a quello che un ciclo 'for' annidato farebbe in * qualsiasi * lingua; non c'è niente di specifico in proposito. –

+0

Ogni volta che voglio fare qualcosa di relativamente semplice come iterare su coppie di valori, Bash rende il carico di lavoro un'epopea insormontabile. Quindi non uso bash quando ho bisogno di qualcosa di più complesso di un elenco di chiamate di shell. – ThorSummoner

risposta

6

Se si desidera utilizzare una variabile e di eseguire e di azione con essa, è sufficiente utilizzare un loop:

for file in 4 5 6 7 8 
do 
    paste "${file}_1" "${file}_2" 
done 

Questo farà

paste 4_1 4_2 
paste 5_1 5_2 
... 
+1

O anche "incolla" $ {file} "_ {1,2}' – rici

+4

Guarda, Ma! Nessun ciclo: 'printf '% s% s \ n' {4..8} _ {1,2} | xargs -I cmd paste cmd' –

+1

Ma l'esecuzione di 'xargs' su valori non quotati si accumula rapidamente se si hanno nomi di file non banali che richiedono quoting o escaping. – tripleee

13

sono d'accordo con la risposta attualmente proposto da fedorqui in il contesto della domanda attualmente posta. Il sotto è dato solo per fornire alcune risposte più generali.

più Un approccio generale (per bash 4.0 o più recente) è quello di memorizzare le coppie in un array associativo:

declare -A pairs=([4_1]=4_2 [5_1]=5_2 [6_1]=6_2 [7_1]=7_2 [8_1]=8_2) 
for i in "${!pairs[@]}"; do 
    j=${pairs[$i]} 
    paste "$i.txt" "$j.txt" >"${i}.${j}.txt" 
done 

Altro (compatibile con le versioni precedenti di bash) è quello di utilizzare più di un array convenzionale:

is=(4_1 5_1 6_1 7_1 8_1) 
js=(4_2 5_2 6_2 7_2 8_2) 
for idx in "${!is[@]}"; do 
    i=${is[$idx]} 
    j=${js[$idx]} 
    paste "$i.txt" "$j.txt" >"$i.$j.txt" 
done 
1

C'è un modello comune in cui si hanno coppie di file, in cui un nome della coppia può essere facilmente derivato dall'altro. Se il file di cui si conosce il nome è X e l'altro file è Y, si hanno i seguenti casi d'uso comuni.

  • Per la ridenominazione, Y è X con un'estensione rimossa e/o un timbro data aggiunto.
  • Per la transcodifica, Y è X con un'estensione diversa e forse una directory diversa.
  • Per molte attività di analisi dei dati, X e Y condividere alcune parti del nome del file, ma avere parametri o estensioni differenti.

Tutti questi si prestano allo stesso scheletro di codice approssimativo.

for x in path/to/base*.ext; do 
    dir=${x%/*} # Trim trailing file name, keep dir 
    base=${x##*/} # Trim any leading directory 

    # In this case, $y has a different subdirectory and a different extension 
    y=${dir%/to}/from/${base%.ext}.newext 

    # Maybe check if y exists? Or doesn't exist? 
    if [ -e "$y" ]; then 
     echo "$0: $y already exists -- skipping" >&2 
     continue 
    fi 

    mv or ffmpeg or awk or whatever "$x" and "$y" 
done 

La chiave qui è l'osservazione che y può essere derivata dalla x con alcune semplici sostituzioni variabili. Quindi si esegue il loop sui valori x e si calcola il valore y corrispondente all'interno del ciclo.

Qui, abbiamo utilizzato la shell integrato ${variable#prefix} e ${variable%suffix} operatori per restituire il valore della variabile con qualsiasi leader prefix o finali suffix, rispettivamente, rifilata. (C'è anche ## e %% per abbinare il più lungo, anziché più breve, possibile corrispondenza. L'espressione dopo # o % è una struttura regolare shell glob.Questi di solito dovrebbero essere tutto ciò che serve, anche se spesso vedi gli script sed o awk anche per questo lavoro banale (dove in realtà dovresti cercare di evitare un processo esterno), oltre naturalmente a trasformazioni più impegnative.

Se avete bisogno di ciclo oltre x file sparsi per directory diverse, forse il ciclo dovrebbe iniziare con qualcosa di simile

find dir1 dir2 etc/and/so/forth -type f -name 'x-files*.ext' -print | 
while IFS='' read -r x; do 
    : 

Un problema comunemente visto in simili domande è risposte che non riescono a citare $x e $y correttamente. Generalmente, qualsiasi variabile contenente un nome di file dovrebbe sempre essere tra virgolette.

dove X e Y sono indipendenti, una soluzione comune è quello di loop su un documento qui contenente la mappatura:

while read -r x y; do 
    : stuff with "$x" and "$y" 
done <<'____HERE' 
    first_x_value first_y_value 
    another_x  corresponding_y 
    random   surprise 
____HERE 
+0

Non riesco davvero a scrivere "X Files" con una faccia completamente dritta. Scusate. – tripleee

1

È possibile utilizzare un dizionario/HashMap:

animals=(dog cat mouse) 
declare -A sound=(
    [dog]=barks 
    [cat]=purrs 
    [mouse]=cheeps 
) 
declare -A size=(
    [dog]=big 
    [cat]=medium 
    [mouse]=small 
) 
for animal in "${animals[@]}"; do 
    echo "$animal ${sound[$animal]} and it is ${size[$animal]}" 
done 

In questo modo, puoi lavorare con tuple (coppie), "triple" ecc. - qualsiasi tipo di dizionario/dati hashMap.

Crediti: L'idea principale è presa dalla risposta di @ @ CharlesDuffy-s.

Problemi correlati