[megoldva] Sed (szerintem)

Sziasztok!

Szeretném egy mappámban található összes fájlban (és a benne lévő mappákban lévő fájlokban is, stb...) bizonyos szavakat átírni másik szavakra...
pl az alma szót körte szóra.
két kérdés: sed-del hogy lehet egy fájlban kicserélni a dolgoka, és nem új fájlt létrehozni?

(for i in `ls`;do echo $i;done ezzel ki tudom iratni az összes fájlomat...)

Köszi!

Hozzászólások

Ha a file-ok és könyvtárak nevében szeretnéd cserélni (az általad felírt ciklusból kiindulva), és nem a tartalmukban, akkor kb így tenném:

for i in $(find /konyvtar/utvonal); do ujnev=$(echo $i | sed -e 's/mit/mire/g'); mv $i $ujnev; done

[szerk:] ha a find paracsot paraméter nélkül adod ki, akkor az aktuális könyvtárat fogja rekurzívan listázni. lsd, man find
---
"A megoldásra kell koncentrálni nem a problémára."

Hogy konstruktív legyek:
- a find kimenetét soronként fordítva feldolgozva talán nem lesz olyan probléma, hogy mire átneveznénk valamit, eltűnik annak a szülőkönyvtára: find $ut | tac
- find + szóközök kezelése problémára én így szoktam ciklust szervezni:
valamiprogramaminekkimenetevan | (read line; while [ -n "$line" ]; do .....; read line; done )


#! /bin/bash

usage()
{
    exit 1
}

chacc()
{
    echo "$@" | sed 's/ /_/g' | \
                sed 's/[öőó]/o/g' | \
                sed 's/[ŐÓÖ]/O/g'| \
                sed 's/[úűü]/u/g'| \
                sed 's/[ÚŰÜ]/U/g'| \
                sed 's/á/a/g'| \
                sed 's/Á/A/g'| \
                sed 's/é/e/g'| \
                sed 's/É/E/g'| \
                sed 's/í/i/g'| \
                sed 's/Í/I/g' | \
                sed 's/_-_/-/g'
}

[ "x$1" == "x" ] && usage

find "$1" | sort -r | \
    while read LINE
do
    CDIR=$(dirname "$LINE")
    FNAME=$(basename "$LINE")

    NNAME="${CDIR}/$(chacc "$FNAME")"
#    echo $NNAME
    [ "$LINE" == "$NNAME" ] ||  mv "$LINE" "$NNAME"
done

de már egyszer ilyesmi kérdés volt

Linux alatt van inline sed is: sed -i --> nem kell az mv
"A herceg én vagyok."

ez jo, ezt a '-i'-t en sem tudtam (valszeg lusta voltam megnezni a manualt)
Viszont a for name in `ls`; csak az adott konyvtarra megy ra, ezzel egyenerteku a
for name in *.
De vigyazni kell, ha szokoz van a filenevben, mert akkor azt ket kulon name-be teszi.
A find-nak van egy jo opcioja erre, termeszetesen kontraintuitiv kicsit.

find innentol/ -type f -exec sed ... {}

ahol a {} -t helyettesiteni fogja a szepen utvonalazott filenevekkel. A '-type f' miatt pedig csak a fileokat fogja listazni.

Ha nincs szokoz, akkor jo a :
for name in `find innentol/` ; do sed -i -e 's/alma/korte' $name ; done

A for name in `ls` és a for name in * egyáltalán nem egyenértékű, utóbbi ugyanis nem szabdal szóközök mentén, persze később valszeg vigyázni kell hogy "$name"-ként (idézőjelek közt) hivatkozz rá.

A find-exec eddig körülbelül száznegyvenhét hup topicban lett részletesen kitárgyalva. Az exec nem rossz, de baromi lassú, mivel minden fájlra külön sed-et indít, holott egy sed akár sok-sok fájlt is képes lehet feldolgozni. Helyette -print0 kombinálva az xargs -0 paranccsal, ami gyors is, és a szóközökön sem száll el.

elég sok "nyelvtannáci" van errefelé és az is aki megsértődik rájuk, egyikőjüket sem értem, de ez az ujj olyan volt mintha az enyémre vágtak volna :)

nem ls hanem pl find, főleg mert az paraméterként tud futtatni (-exec ... {}\;)
ideiglenes fájl kell, (legalábbis nagyon nem érdemes helyben, kivéve ha egyetlen 4gigás fájlod van de az ugye más tészta), elég egyetlen (akár mktemp-el) létrehozott)
(van egy olyan előnye hogy észlelt bibi esetén nem vész el az adat)

ps: már megint nem frissítettem válasz előtt :)
pps: ha már find, ne for hanem .. ahogy írtam :)

Helyben módosítás kérdésköre:

A sed -i kapcsolója is ideiglenes fájlt hoz létre, nem tud mást csinálni, az "alma" szót fizikailag lehetetlenség helyben "körte" szóra cserélni, mivel a fájl teljes további tartalmát arrébb kell mozgatni. Tehát a "sed -i" egy kényelmi szolgáltatás, nem kell neked ideiglenes fájllal vacakolni. És mint ilyen kényelmi dolog, nagyon hasznos és nagyon jó.

Helyben módosítás elvileg megoldható, ha a cserélendő és az új sztring azonos hosszúságú, azonban hirtelen nem tudok olyan progiról, ami ezt megoldaná. Biztos össze lehet rá tákolni valami perl szörnyűséget, vagy írni egy C progit.

mint kényelmi szolgáltatás lehet használni, én mint általánosságban írtam, azaz HA tud valami ellenőrzést a kész fájlon, tehát a sed-et ellenőrizendő, azaz nem bízzuk a sed-re hogy jól csinálta-e, pl mert nem csak azt lehet ellenőrizni hogy a sed azt csinálta-e amit mondtunk neki (mert mittudomén nem bugos, nem írt más program bele, stb), hanem hogy azt mondtuk neki amit szerettünk volna, azaz magunkat

- azért nem néztem utána mert egyrészt nekem nem kellett, másrészt pedig elég biztos voltam a dolgomban, viszont anélkül hogy tudnám nem írom azt hogy "úgy van és pont"

- megnéztem és nem csak hogy igazam lett, hanem ráadásul neked kell megadnod a backup fájl nevét, azaz pl mktemp-el együtt kell (általános esetben) használni

Kanyarodjunk vissza az eredeti kérdéshez egy picit... azt írta Koli, hogy "mappámban", nemde? Továbbá hogy rekurzívan, fene tudja hány fájlról van szó, lehet hogy százezerről.

Örülök, hogy ennyire ragaszkodsz az mktemp-hez a sokkal egyszerűbb megközelítés helyett. Össze tudnál rakni nekem egy teljes parancssort mktemp-estül? Csak úgy, kíváncsiságból... :)

igazad van, a feladattól függ hogy kell megoldani, a szál második hozzászólásomban írtam hogy onnantól már elméleti síkon mozogtam:)

egyébként nem tudom mi olyan bonyolult:
for i in $(valami lista); do
a=$(mktemp "izlés szerinte megadni hogy hova, a fájlnevetpedig kitalálja" )
sed ... $i > $a
if [ mindneoké ]; then mv -f $i $a;
else rm $a; fi
done

értelemszerűen a "mindenoké" a "sed -i"-vel ekvivalensen lehet maga a sed visszatérési értéke, amiért a mktempet ELVIEKBEN említettem az az hogy "mindenoké" lehet pl egy grep ami megszámolja hogy tényleg prímszámú alma szerepel-e benne, ellenőrizve nem csak hogy a rendszer jól dolgozott-e hanem hogy a mi kitalált sed kifejezésünk jó-e

pps: bocsi a szerkesztésegetésekért :)

Jaaaaa, megvan mit értettem félre. Azt hittem, az ideiglenes fájlt sokáig meg akarjuk tartani, mert a mindenoké programot majd az egész rakat fájl átalakítása után fogjuk kézzel futtatni :-) Te úgy gondoltad, hogy rögtön utána, aztán el is takarítva az ideiglenes fájlt. Így már teljesen értem a megközelítésedet. Mondjuk nekem még nem akadt szükségem erre, de fene tudja... bármikor jól jöhet.

ez most komoly?
egy részmegoldás a man önelolvasódása, ott van az első oldalon (tmpdir) hogy lehet a könyvtárat megadni (sokszor praktikus az eredeti fájlrendszerbe, könyvtárba, tehát /eredeti/hely/eredetifile.bakXXX-szerű formában, mert nem másolni törölni kell ha gond van hanem csak átnevezni..), azt hogy /tmp-be miért nem tudsz írni, hmm ezt már neked kell végigjárni:)

sed -i 's/ alma / korte /g' $(grep --color=never -rl ' alma ' /az/en/kicsi/mappam)
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

NAME
rename - renames multiple files

SYNOPSIS
rename [ -v ] [ -n ] [ -f ] perlexpr [ files ]

DESCRIPTION
"rename" renames the filenames supplied according to the rule specified as the
first argument. The perlexpr argument is a Perl expression which is expected to
modify the $_ string in Perl for at least some of the filenames specified. If a
given filename is not modified by the expression, it will not be renamed. If no
filenames are given on the command line, filenames will be read via standard
input.

Ami a válasz a kérdésemre:

#! /bin/bash
for i in `find`;
do
sed -i 's/alma/korte/g' $i;
done