Sziasztok!
Egy olyan kérdésem lenne, hogy ha van a és b fájlom és mondjuk a 2. sora után be akarok szúrni 2 sort b-ből, mindezt egy harmadik fájlba, akkor ezt hogyan tudom megtenni?
pl.
a tartalma:
1
2
3
4
5
6
b tartalma:
11
22
33
44
55
66
Tehát akkor a fentiek alapján szeretnék egy c fájlt, amiben ez áll:
1
2
11
22
3
4
33
44
5
6
55
66
Nagyon köszönöm a segítséget!
Sziasztok!
- 1482 megtekintés
Hozzászólások
Haaaat, ilyen beepitet parancsrol nem tudok, viszont ezt barmely programnyelven viszonylag egyszeruen meg lehet oldani.
Azert merulnek fel kerdesek: mi legyen, ha a ket file nem egyforma hosszu? mekkorak a file-ok (ha nagyok, akkor celszeru valami buffert kialakitani)? soronkent vagy bajtonkent (esetleg maskepp: ketsoronkent?) kell-e "osszevegyiteni"?
/sza2
- A hozzászóláshoz be kell jelentkezni
Kedves sza2king!
Szerintem awk-ban lenne célszerű megírni, vagy sima shell scriptként, csak elég kezdő vagyok a témában és nem tudom hogyan kezdjek neki.
Az tudom, hogy awk addig nézi a fájlokat, amíg tartanak, ott elvileg nem számítana a sorok száma.
Üdv!
- A hozzászóláshoz be kell jelentkezni
Ha a ke't file
a.dat
e's
b.dat
:
awk -v f=b.dat '{ print; if ( n%2 ) { getline < f; print; getline < f; print; } n++; }' a.dat
- A hozzászóláshoz be kell jelentkezni
Kedves apal!
awk -v f=b.dat '{prinnt; if ( n%2 ) { getline < f; print; getline < f; print; } n++ }' a.dat
Hatására ezt kapom:
11
22
33
44
55
66
Mi lehet a hiba?
- A hozzászóláshoz be kell jelentkezni
prinnt => print
- A hozzászóláshoz be kell jelentkezni
filea="filea.txt"
fileb="fileb.txt"
nlines=`cat "$filea"|wc -l`
lineno=0
while [ $nlines -ge $lineno ]
do
tail "$filea" -n +$lineno | head -n2>>outputfile
tail "$fileb" -n +$lineno | head -n2>>outputfile
lineno=$(($lineno+2))
done
Nye! Tesztelve, muxik, johet a csoki!
- A hozzászóláshoz be kell jelentkezni
Ordo(n^2)-es, nagy file-oknal ronggya' daralja a diszket, de egyebkent tenyleg jo.
szerk: az olta'son kivul: azert tenyleg megvan ez a hatranya a shell-nek hogy sok esetben relative konnyu megoldast talalni, de az minden lesz, csak nem hatekony... en vmi ket program kozos pipe-ba ontogetes + `cat -n` + `sort -n` komboval gondolkodtam eloszor, de az sem az igazi :] (ott pl tmpfile-ok kellettek volna).
- A hozzászóláshoz be kell jelentkezni
Igazad van. Kerdes hanyszor kell elvegezni a feladatot. Meg aztan kodolni farasztobb mint kavezni egyet amig fut. A diszk meg daraljon csak, azert van ;)
- A hozzászóláshoz be kell jelentkezni
Egy ilyen mondatért engem agyoncsaptak volna anno...
- A hozzászóláshoz be kell jelentkezni
Miert is?
Kerdes mi a fontosabb. Eltolteni egy orat egy idealis C/perl/akarmi program megirasaval, vagy osszedobni a fenti szkriptet 10 perc alatt, es nezni/mason dolgozni amig fut.
- A hozzászóláshoz be kell jelentkezni
Engem úgy tanították, hogy mielőtt nekiállnék kódolni, gondoljam át a problémát, és készítsek fejben egy megoldást. Ha az megvan, csak akkor nyúljak a géphez.
A vázolt probléma triviális. Meg kell nyitni két fájlt, be kell olvasni két sort az egyikből, ki kell írni, két sort a másikból, azt is ki kell írni, majd ha az egyik véget ér, akkor be kell fejezni a folyamatot. A kérdés, hogy mi legyen a maradék sorokkal, meg mi legyen akkor, ha az egyik file hosszabb mint a másik, de ezek nem voltak definiálva.
Ha így végig van gondolva, akkor a megoldás adja magát, választani kell egy tetszőleges nyelvet, és le kell kódolni. Az egyszerűség kedvéért a bash-t válaszottam mint környezetet, de tény, hogy szinte csak jobb választás van. A kérdés csak az, hogy ezer vagy milliós nagyságrendű sorról van szó, meg hány file, ilyesmik, ez alapján lehet érdemes a nyelvet kiválasztani.
- A hozzászóláshoz be kell jelentkezni
Ja, és nem vagyok kóder, az egy ijfúkori ballépés volt, amikor programozással foglalkoztam :D
- A hozzászóláshoz be kell jelentkezni
Meg mindig igazad van.
En azert lattam neki a dolognak, mert kivancsi mezei unix parancsokkal hogyan oldhato meg a dolog. Tudom, az awk meg a perl szeles korben elterjedt, megis hasznalatuk sokkal bonyolultabb mint a sima parancsoke. Aztan hamar kimazsolaztam a megoldast, gondoltam posztolok egyet.
A hanyag hozzaallas ebben a kornyezetben (mas hazifeladatat megoldani a weben) nalam elfogadhato :)
Mindenesetre az alabbi "exec" -es megoldasod lenyugozott. Nem is gondoltam volna, hogy igy is lehet :)
- A hozzászóláshoz be kell jelentkezni
+1. A diszk nem kávédaráló, az I/O drága, c-ben húsz sorból magyarázatokkal együtt kijön :)
- A hozzászóláshoz be kell jelentkezni
Kedves Foltos!
Köszönöm a megoldást!
Ezek szerint ha használnék egy lineno1 és egy lineno2 változót, akkor tetszőlegesen állíthatnám, hogy mondjuk a-ból 5 , b-ből 10 sort rakjon be, és így tovább?
Köszönöm!
Üdv!
- A hozzászóláshoz be kell jelentkezni
Megfelelo modositassal elerheto amit szeretnel. De ketlem, hogy a lineno1 es lineno2 bevezetese eleg lenne :)
- A hozzászóláshoz be kell jelentkezni
Nem szép megoldás, inkább csak érdekességképp:
#!/bin/bash
exec 5< a
exec 6< b
while read Line <&5
do
echo $Line
read Line <&5
echo $Line
read Line <&6
echo $Line
read Line <&6
echo $Line
done
exec 5>&-
exec 6>&-
- A hozzászóláshoz be kell jelentkezni
Ez cool :)
- A hozzászóláshoz be kell jelentkezni
Kedves Fisher!
Köszönöm a Te megoldásodat is!
Ahogy látom, ezzel a legkönnyebb a manuális beállítása annak, hogy mennyi sort vegyen innen és onnan.
Mégegyszer köszönöm mindenkinek a segítségét, nem házi feladat megoldásnak kell, hanem önállóan próbálok előre jutni unix környezetben és ez sokszor nem egyértelmű.
- A hozzászóláshoz be kell jelentkezni
Megjegyzés. De, szép. (Bár szerintem a lezárásnál is kisebb-jel illenék - igaz, magát ezt a konstrukciót a bash doksi nem is említi (csak az egyéb shell man-ok), és valószínűleg nincs is jelentősége.)
- A hozzászóláshoz be kell jelentkezni
Valóban, azt kissé elkapkodtam.
Egyébként tényleg fölösleges, a nyitva maradt descriptorokat úgyis lezárja a shell, de így van eleje és vége.
Viszont ami nagyobb baj, hogy az
echo $Line
sor helyett inkább
echo "$Line"
kéne. Vagy inkább echo "${Line}". Ez csak este jutott eszembe, a metrón hazafelé.
Amúgy: http://tldp.org/LDP/abs/html/
Itt a part 5 - 19. rész foglalkozik ilyesmikkel.
- A hozzászóláshoz be kell jelentkezni
Ha egyesével akarod összefésülni, akkor egy "funkcionális" (nem imperatív) megoldás (kettőnél több file-ra is), GNU utility-kkel:
for F in F1 F2 ... Fn; do
cat --number -- "$F"
done \
| sort --stable --key=1,1n \
| cut --fields=2-
- A hozzászóláshoz be kell jelentkezni
Köszönöm Lacos!
Egyesével való összefűzésre nagyon jól működik.
- A hozzászóláshoz be kell jelentkezni
Én is hozzáteszem a magamét, azzal együtt, hogy biztos, hogy a fenti exec-es dolgot használnám. Sajnos ezzel az innen is kettő, onnan is kettő kitétellel eléggé megnehezítetted a dolgot :)
mkfifo 1 2
paste -d X - - < a.txt > 1 &
paste -d X - - < b.txt > 2 &
paste -d X 1 2 | tr X '\n'
rm 1 2
nem szép, de lagalább más, mint az eddigiek. Persze vannak előfeltételek a kódban (pl. a bejövő adatokban ne legyen X, illetve a -d opcióban megadott karaktert úgy kell megválasztani, hogy azzal ne legyen gáz - mondjuk sima szövegfájlnál pl. lapdobás-jel nem nagyon lenne.)
- A hozzászóláshoz be kell jelentkezni
Kedves Zahy!
Köszönöm, a Te megoldásod is nagyon jó, látom itt named pipe-okat használsz.
- A hozzászóláshoz be kell jelentkezni
$ paste a.dat b.dat | sed '$!N;s/\n/\t/' | awk 'BEGIN{OFS="\n"} {print $1, $3, $2, $4}'
- A hozzászóláshoz be kell jelentkezni
Köszönöm pme a Te megoldásodat is!
Ez is teljesen jól működik. Ha eltérő a két fájl hossz, akkor üres helyek lesznek és végül is azt egy újabb awk-s futtatással ki lehet szedni.
- A hozzászóláshoz be kell jelentkezni
A valtozatossag kedveert itt egy megoldas perlben, tetszoleges szamu soronkent fesul, tetszoleges szamu file-t, az se zavarja, ha nem egyforma hosszuak a file-ok, vagy valamelyik file-ban a sorok szama nem oszthato n-nel. Nem ugraltatja foloslegesen a diszket, es nem olvassa be a teljes file-okat memoriaba. A hianyzo sorok helyett '<<after-eof>>'-ot printel.
Igy kell hasznalni:
$ comb -n <LINES> FILE1 [FILE2 [FILE3 ...]]
#! /usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
getopts( 'n:', \my %opt );
my $lines_at_once = $opt{n} // 1;
my @file_refs;
for (@ARGV) {
my $file_ref = { NAME => $_, FH => undef, EOF => 0 };
open $file_ref->{FH}, '<', $file_ref->{NAME}
or die "open '$file_ref->{NAME}': $!";
push @file_refs, $file_ref;
}
while ( grep { not $_->{EOF} } @file_refs ) {
last if ( @file_refs == grep { eof $_->{FH} } @file_refs );
for (@file_refs) {
my $n = 0;
my $fh = $_->{FH};
while ( $n++ < $lines_at_once ) {
my $line = <$fh>;
if ( not defined $line ) {
$_->{EOF} = 1;
$line = '<<after-eof>>' . $/;
}
print $line;
}
}
}
__END__
- A hozzászóláshoz be kell jelentkezni
Kedves Rubasov!
A Te megoldásodat is köszönöm, itt is egy dolog foglalkoztat: mi van akkor ha különböző sormennyiségeket akarok beolvasni a bemeneti fájlokból?
Perl-t még annyira sem ismerem mint az Awk-t, de azt gondolom, hogy akkor nem csak egy -n kapcsoló kellene, hanem -n1 -n2 .... ahány fájl van, de a while utáni részt őszintén szólva még nem értem.
Kérhetném, hogy írj oda kommenteket?
Előre is köszönöm!
- A hozzászóláshoz be kell jelentkezni
Itt egy kommentelt valtozat, remelem igy ertheto:
#! /usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
# A '-n' parameter (pozitiv egesz) erteket var, ez az $opt{n}-be kerul.
getopts( 'n:', \my %opt );
# Ha nem volt '-n' a parancssorban, akkor a default legyen '-n 1'.
my $lines_at_once = $opt{n} // 1;
# A file-okkal kapcsolatos infokat egy tombben tartom, aminek az elemei
# hash referenciak, az elso file-ra pl igy neznek ki az elemek:
# $file_refs[0]{NAME} - a file neve
# $file_refs[0]{FH} - file handle
# $file_refs[0]{EOF} - flag valtozo, hogy elertuk-e mar a file veget
# Fontos, hogy a @file_refs a legkulso scope-ban legyen deklaralva, kulonben
# a perl a scope vegen automatikusan meghivja a close($fh)-t.
my @file_refs;
# Minden file argumentumra...
for (@ARGV) {
# Init $file_refs[$_]
my $file_ref = { NAME => $_, FH => undef, EOF => 0 };
# Nyisd meg a file-t olvasasra.
open $file_ref->{FH}, '<', $file_ref->{NAME}
or die "open '$file_ref->{NAME}': $!";
push @file_refs, $file_ref;
}
# Amig van olyan file, aminek meg nem ertunk a vegere...
# A feltetelben szereplo 'grep ...' skalar kontextusban az ilyen file-ok
# szamaval ter vissza.
while ( grep { not $_->{EOF} } @file_refs ) {
# Ez a sor egy specialis eset kezelese, konkretan:
# Ha minden file egyforma hosszu es n-nel oszthato szamu sor van bennuk
# akkor nem eleg utolag ellenorizni, hogy elertuk az EOF-ot, hanem tudnom
# kell a kulso while ciklus elejen, hogy a kovetkezo olvasas mindegyiken
# EOF-ot fog adni, pont ezt csinalja az 'eof $fh'.
last if ( @file_refs == grep { eof $_->{FH} } @file_refs );
# Mindegyik file-ra...
for (@file_refs) {
# Az aktualis n-soros "blokkbol" eddig beolvasott sorok szama.
my $n = 0;
# Ez csak egy segedvaltozo, hogy kesobb ne kapjak syntax errort erre:
# <$_->{FH}>
my $fh = $_->{FH};
# Amig be nem olvastam a szukseges n sort...
while ( $n++ < $lines_at_once ) {
my $line = <$fh>;
# Ha az '<>' operator undeffel tert vissza, akkor elertem a file veget.
if ( not defined $line ) {
# Jegyezd ezt meg, hogy a kulso while ciklus a feltetel
# kiertekelesekor tudhassa.
$_->{EOF} = 1;
# Es helyettesitsd a hianyzo sort, hogy a "tablazatos" forma ne
# seruljon.
# Ezt persze helyettesithetned ures sorral, vagy tetszoleges olyan
# ertekkel, ami garantaltan nem szerepel egyik file-odban sem.
# Illetve, ha peldaul azt szeretned, hogy a kimenet a legrovidebb
# file-hoz igazodjon, akkor itt kene 'last OUTER WHILE'-t hasznalni,
# de ehhez egy atmeneti tombben tarolni kene az osszes file utolsonak
# olvasott "blokkjanak" osszes sorat, hiszen nem tudhatod elore, hogy
# egy kesobb olvasott file nem bizonyul-e rovidebbnek az aktualisnal.
# A kimenet legrovidebb file-hoz igazitasa egyebkent utolag is egesz
# egyszeruen megoldhato, csak csapd le a kimenet veget onnantol, hogy
# egy a (file-ok szama) * (sorok szama) "blokk"-ban szerepel egy
# '<<after-eof>>'.
$line = '<<after-eof>>' . $/;
}
print $line;
}
}
}
__END__
A masik kerdesedre valaszolva pedig:
Ha file-onkent kulonbozo szamu sort szeretnel fesulni, akkor kb. a kovetkezokre lenne szukseged.
- olyan parancssort dolgozz fel, hogy
$ comb FILE1 N1 [FILE2 N2 [...]]
- ez alapjan vegyel fol egy uj elemet a
$file_refs[$_]
-be, mondjuk igy:
$file_refs[$_]{N} = ...
- majd hasznald ezt az erteket a belso while ciklus felteteleben:
while ( $n++ < $_->{N} )
- A hozzászóláshoz be kell jelentkezni
cat file1.text file2.text |sort -un > file3.text
(bár nem teszteltem)
- A hozzászóláshoz be kell jelentkezni
ebben az igen speciális esetben talán működik, de nézzünk egy általánosabbat:
egyik fájl:
a
d
g
w
h
z
Másik:
fgf
sjd
ert
dfg
jhh
Amiből:
a
d
fgf
sjd
g
w
ert
dfg
h
z
jhh
Kell a kimeneten :-P
- A hozzászóláshoz be kell jelentkezni
hm... talán:
cat file1.text file2.text |sort -ud > file3.text
- A hozzászóláshoz be kell jelentkezni
Gondold át mégegyszer... Nem tartom véletlennek azt, hogy egészen másfajta megoldásokkal álltak itt elő a shellguruk :-)
- A hozzászóláshoz be kell jelentkezni
Kisse unatkoztam, ugyhogy itt van meg egy lehetoseg, azok kedveert, akik ertekelik az ilyet.
Regebben, amikor meg nem ismertem semmilyen epkezlab programozasi nyelvet, az ilyesmi feladatokat tipikusan vi makrokkal oldottam meg. No para, meglepoen egyszeru.
Eloszor generaljunk ket tesztfile-t:
$ for i in $(seq 0 9); do echo a$i >> testa.txt ; echo b$i >> testb.txt ; done
Inditsunk egy vi-t (konkretan nvi-t, mert kisebb valtoztatasokkal ugyan megoldhato vim-ben is, de ott valamiert fajdalmasan lassu).
$ nvi testa.txt
Miutan fut a vi, nyissuk meg osztott ablakban a masik file-t is:
:E testb.txt<CR>
Az ablakok kozott ^w (control+w)-vel valthatunk, valtsunk is vissza az elso ablakra. (A kurzor mindket ablakban az elso sor elso karakteren all.)
Probaljuk ki eloszor, mit csinalnank, hogy az elso ket sort atmasoljuk az elso ablakbol a masodikba:
d2d # ket sort torol es a paste bufferbe tesz
^w # valt a masik ablakba
P # paste-eli a buffer tartalmat az aktualis sor fole
4j # a kurzort oda viszem, ahol a kovetkezo 'P' kovetkezne
^w # vissza az elso ablakba
Ez eddig jo, de ugye nem akarjuk kezzel csinalni? ;-)
Allitsuk vissza a ket file eredeti tartalmat
:e!<CR>
(mindket ablakban), majd irjunk egy rekurziv makrot az elozoekbol:
:map g d2d^wP4j^wg<CR>
Arra ugyeljunk mikozben a fenti makrot visszuk be, hogy bizonyos parancsokat (^w, ^c, ^v, escape) escape-elni kell ^v (control+v)-vel, vagyis egy ^w-t igy vigyunk be: ^v^w.
Minden elkeszult, alljunk az elso file elso sorara a kurzorral, majd futassuk a friss makronkat (nyomjuk meg a 'g'-t).
Ta-daam.
Mar csak el kell mentenunk a masodik ablak tartalmat:
:w out.txt<CR>
Aztan lepjunk ki az ablakokbol mentes nelkul:
:q!<CR>
(ketszer)
Ez a leiras kisse hosszadalmas lett ugyan, de a vi makrok erosen write-onlyk, szoval ha csak a makrot irtam volna le, senki nem ertene, mit is csinal. Valojaban viszont nincs benne semmi bonyolult, csak ismerni kell a vi parancsokat. Es ha lehetek kisse nagyvonalu, hogy nem szamolom az elokeszuleteket, akkor a kod hossza magasan veri az osszes tobbi megoldast.
- A hozzászóláshoz be kell jelentkezni
Kedves rubasov!
Köszönöm Neked ezt a megoldást, igyekszem ezt is kipróbálni!
- A hozzászóláshoz be kell jelentkezni