File1
obsah:
line1-file1 "1"
line2-file1 "2"
line3-file1 "3"
line4-file1 "4"
File2
obsah:
line1-file2 "25"
line2-file2 "24"
Pointer-file2 "23"
line4-file2 "22"
line5-file2 "21"
Po spuštění skriptu Perl/Shell, File2
obsah by se měl stát:
line1-file2 "25"
line2-file2 "24"
line1-file1 "1"
line2-file1 "2"
line3-file1 "3"
line4-file1 "4"
Pointer-file2 "23"
line4-file2 "22"
line5-file2 "21"
tj. vložte obsah File1
v File2
před řádek, který obsahuje „Ukazatel“.
sed
má k tomu funkci a může provádět úpravy inline:
sed -i -e '/Pointer/r file1' file2
Tím se však váš řádek umístí nad soubor1. Chcete-li ji umístit níže, zpoždění řádku výstup:
sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2
S GNU sed
sed '/Pointer/e cat file1' file2
Podle manuálně pro e [command]
Všimněte si, že na rozdíl od příkazu r bude výstup příkazu vytištěn okamžitě; příkaz r místo toho zpožďuje výstup do konce aktuálního cyklu.
Bez použití sed
nebo awk
...
Nejprve najděte linii, na které je váš vzor:
line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)
Poté použijte 3 příkazy k výstupu požadovaného výsledku:
{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file
To má tu nevýhodu, že k souboru 3krát přistupujete file2
, ale může být jasnější než řešení sed
of awk
.
awk
to značně usnadňuje.
Vložte řádek před soubor:
awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile
Chcete-li, aby se vnitřní soubor tiskl za řádkem Pointer
, stačí změnit pořadí vzorů (pro získání výchozí akce je třeba přidat středník) a můžete vypustit proměnnou line
:
awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile
A jen proto, že ještě nikdo nepoužil Perl
,
# insert file before line
Perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile
# after line
Perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile
Snadná úloha pro ed
:
ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN
-r file1
přečte v zadaném souboru až za adresovaný řádek, což je v tomto případě řádek předcházející prvnímu řádku odpovídajícímu Pointer
. Vloží se tedy obsah file2
pouze jednou, i když Pointer
se vyskytuje na více řádcích. Pokud jej chcete vložit před každý odpovídající řádek, přidejte g
lobal příznak:
ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN
Nahradit ,p
s w
, pokud chcete soubor upravit na místě.
Přijatá odpověď sed
funguje ve většině případů, ale pokud je značka na posledním řádku, příkaz nebude fungovat podle očekávání: vloží obsah File1
za značku.
Původně jsem se pokusil:
sed '/Pointer/{r file1
N}' file2
což také funguje dobře (protože r
udělá svou magii na konci cyklu), ale má stejný problém, pokud je značka na posledním řádku (po posledním řádku není žádný řádek N
ext) řádek). Chcete-li tento problém vyřešit, můžete do svého vstupu přidat nový řádek:
sed '/Pointer/{ # like the first one, but this time even if the
r file1 # marker is on the last line in File2 it
N # will be on the second to last line in
} # the combined input so N will always work;
${ # on the last line of input: if the line is
/^$/!{ # not empty, it means the marker was on the last
s/\n$// # line in File2 so the final empty line in the
} # input was pulled i\n: remove the latter;
//d # if the line is empty, delete it
}' file2 <(printf %s\\n)
Tím vložíte file2
obsah před každým odpovídajícím řádkem. Chcete-li jej vložit pouze před první odpovídající řádek, můžete použít oop l
a jednoduše vytáhnout řádek extenze n
, dokud se nedostanete na konec souboru:
sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)
S těmito řešeními sed
ztratíte možnost úpravy na místě (můžete však přesměrovat na jiný soubor).
K načtení řádků v souboru2 použijte smyčku. Pokud najdete řádek začínající na Pointer
, vytiskněte soubor1. Toto je zobrazeno níže:
#!/bin/bash
while IFS= read -r line
do
if [[ "$line" =~ ^Pointer.*$ ]]
then
cat file1
fi
echo "$line"
done < file2
Existuje několik způsobů, jak to udělat w/sed
. Jedním ze způsobů je odložené čtení, jak je doporučeno v přijaté odpovědi. Mohlo by to být také psáno jako:
sed -e '$!N;P;/\nPointer/r file1' -e D file2
... s trochou explicitního pohledu dopředu namísto pohledu implementovaného jinde s vyrovnávací pamětí hold. To bude nevyhnutelně mít stejný problém s posledním řádkem, který si všimne @don_crissti, protože N
dělá zvyšuje řádkový cyklus a příkaz r
ead se používá podle čísla řádku.
Můžete to obejít:
echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -
Ne všechny sed
s interpretují -
znamená standardní vstup, ale mnoho ano. ( říká POSIXsed
by měla podporovat -
znamená standardní vstup, pokud implementátor chce -
znamená standardní vstup ???)
Dalším způsobem je zpracování připojeného obsahu v pořádku. Existuje další příkaz, který plány výstup stejným způsobem r
ead provede, a sed
použije a r
ead v aby byly skripty. Je to ale trochu více zapojeno - vyžaduje to použití jednoho sed
až a
ppendování Pointer
shody s výstupem jiného sed
ve skriptu .
sed ' /Pointer/!d #only operate on first match
s/[]^$&\./*[]/\\&/g;H #escape all metachars, Hold
s|.*|/&/!p;//!d|p;g #print commands, exchange
s|.|r file1&a\\&|;q' file2| #more commands, quit
sed -nf - file2 #same input file
Takže první sed
v podstatě zapisuje druhý sed
skript, který druhý sed
čte na standard-input (možná ...) a platí postupně. První sed
funguje pouze pro první shodu pro Pointer
nalezené a poté q
uits vstup. Jeho úkolem je ...
s/[]^$&\./*[]/\\&/g;H
sed
bude muset interpretovat každý bit, který přečte, doslova, aby to bylo správné. Po dokončení vložte kopii do H
old prostoru.s|.*|/&/!p;//!d|p; x
sed
to p
rint každý vstupní řádek !
ale /&/
jeden, který jsme právě zabezpečili; a potom d
elete všechny stejné. p
zadejte příkazy na druhém sed
, poté ex
změňte h
old a vzorové vyrovnávací paměti, aby pracovaly na naší uložené kopii.s|.|r file1&a\\&|p;q
\n
ewline, protože sed
bude mít připravený jeden, když H
eld line předtím. Vložíme tedy příkaz r file1
a sledujte to s naším \n
ewline, pak příkaz a\\
pro a
ppend následované také \n
ewline. Celý zbytek naší H
eld linie následuje poslední \n
ewline.Skript, který první píše, vypadá asi takto:
/Pointer-file2 "23"/!p;//!d
r file1
a\
Pointer-file2 "23"
V podstatě druhý sed
vytiskne každý řádek, ale ten, který první sed
nastaví na a
ppend. Pro tento konkrétní řádek dva zpožděné zápisy do standard-out jsou naplánováno - první je r
ead of file1
a druhá je kopie řádku, který chceme po něm. První doktorské sed
není v tomto případě vůbec nutné (viz? Žádná zpětná lomítka), ale je důležité bezpečně uniknout tak, jak to dělám, kdykoli se shoduje vzor je znovu použit jako vstup.
Každopádně ... existuje několik způsobů.
[Vložte obsah souboru do jiného souboru PŘED vzorem]
sed -i '/PATTERN/r file1' -e //N file2
[Po vzoru]
sed -i '/PATTERN/r file1' file2
můj preferovaný způsob: templating.
sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile
Toto nahradí každý výskyt CHANGEME v origfile obsahem file2insert. Odstraňte poslední g ze sedu, abyste nahradili pouze první výskyt CHANGEME .
U AWK je to celkem jednoduché:
File1 do File2 before pattern = "Ukazatel"
Nejprve načtěte obsah File1 do proměnné
f1="$(<File1)"
pak proveďte vložení
awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2
(Nebo, pokud chcete vložit File1 za „Ukazatel“)
awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2
awk '/Pointer/{system("cat File1")}1' File2