Bash how to get text in file [closed] - bash

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I file /tmp/txt
contents of the file: aaa aaa aaa _bbb bbb bbb
I need to save the file /tmp/txt_left: aaa aaa aaa
I need to save the file /tmp/txt_right: bbb bbb bbb
!!! attention seeking solutions without the use of variables !!!

awk -F '_' '{print $1> "/tmp/txt_left"; print $2 > "/tmp/txt_right" }' /tmp/txt

You could try cutting the line, slitting on the underscore
Cat /tmp/txt | cut -d_ -f 1 > txt_left

A sed way:
Shorter and quicker:
sed -ne $'h;s/_.*$//;w /tmp/txt_left\n;g;s/^.*_//;w /tmp/txt_right' /tmp/txt
Explained: It could be written:
sed -ne '
h; # hold (copy current line in hold space)
s/_.*$//; # replace from _ to end of line by nothing
w /tmp/txt_left
# Write current line to file
# (filename have to be terminated by a newline)
g; # get (copy hold space to current line buffer)
s/^.*_//; # replace from begin of line to _ by nothing
w /tmp/txt_right
# write
' /tmp/txt
Bash as bash
This is not a real variable, I use first argument element for doing the job and restore argument list once finish:
set -- "$(</tmp/txt)" "$#"
echo >>/tmp/txt_right ${1#*_}
echo >>/tmp/txt_left ${1%_*}
shift
I unshift the string at first place in argument line,
do operation on $1, than shift the argument line so no variable is used and in fine, the argument line return in his original state
... and this is a pure bash solution ;-)

Using bash process substitution, tee, and cut:
tee -a >(cut -d _ -f 0 > /tmp/txt_left) >(cut -d _ -f 1 >/tmp/txt_right) < /tmp/txt

Related

awk script for copy/paste part of string [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 21 days ago.
This post was edited and submitted for review 21 days ago.
Improve this question
Problem:
I have several config files (ProgramName.conf) that need to be edited with awk or sed. The first line contains several letters in place after ":" and end with "]" ProgramName. Or copy filename (it's same as ProgramName). I need to copy ProgramName to the 4th between "command=docker-compose run --rm --name" and "artisan". After conteiner name always word "artisan" in place.
Upd
Thanks for answers, problem solved!
input:
[program:ProgramName]
process_name=
directory=
command=docker-compose run --rm --name artisan some text
target output:
[program:ProgramName]
process_name=
directory=
command=docker-compose run --rm --name ProgramName artisan some text
If ed is available/acceptable, something like:
#!/bin/sh
ed -s file.txt <<-'EOF'
/^\[program:.*\]$/t/^command=/
s/^\[.*:\(.*\)\]$/ \1/
-;+j
,p
Q
EOF
In one-line
printf '%s\n' '/^\[program:.*\]$/t/^command=/' 's/^\[.*:\(.*\)\]$/ \1/' '-;+j' ,p Q | ed -s file.txt
Change Q to w if in-place editing is required.
Remove the ,p to silence the output.
gawk -i inplace '
BEGIN { RS = ORS = ""; FS = OFS = "\n" }
match($1, /^\[program:(.*)\]/, a) {
for (i = 2; i <= NF; ++i)
if ($i ~ /^command=docker-compose run --rm --name/)
$i = "command=docker-compose run --rm --name " a[1]
}
{ print $0 RT }' file
Study the GNU Awk manual for details.
I would harness GNU AWK for this task following way, let file.txt content be then
[program:ProgramName]
process_name=
directory=
command=docker-compose run --rm --name
then
awk 'BEGIN{FS="]|:|\\["}/^\[program:/{name=$3;n=NR}NR==n+3{$0=$0 " " name}{print}' file.txt
gives output
[program:ProgramName]
process_name=
directory=
command=docker-compose run --rm --name ProgramName
Explanation: I inform GNU AWK that field separator (FS) is ] or : or [ then for line starting with [program: I store third column value as name and number of row (NR) as n, then for line which is three lines after thtat happen i.e. number row equals n plus three I append space and name to line and set that as line value, for every line I print it. Be warned that it does modify
to the third line
to comply with your requirement, therefore make sure it is right place do change for all your cases.
(tested in GNU Awk 5.0.1)

How to grab text after newline in a text file no clean of spaces, tabs [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Assume this: It needs to pass a file name as an argument.
This is the only text I’m showing. The remaining text has more data (not shown). The problem: The text is semi-clean, full of whitespace, tabs, Unicode, isn't clean and has to be like this (my needs), so copy/paste this exact text doesn't work (formatted by markup):
I have some text like this:
*** *
more text with spaces and tabs
*****
1
Something here and else, 2000 edf, 60 pop
Usd324.32 2 Usd534.22
2
21st New tetx that will like to select with pattern, 334 pop
Usd162.14
*** *
more text with spaces and tabs, unicode
*****
I'm trying to grab this explicit text:
1 Something here and else, 2000 edf, 60 pop Usd324.32
because of the newline and whitespace, the next command only grabs 1:
grep -E '1\s.+'
Also, I have been trying to make it with new concatenations:
grep -E '1\s|[A-Z].+'
But it doesn't work. grep begins to select a similar pattern in different parts of the text:
awk '{$1=$1}1' #done already
tr -s "\t\r\n\v" #done already
tr -d "\t\b\r" #done already
How can I grab:
grab one newline
grab the whole second line after one newline
grab the number $Usd324.34 and remove Usd
You can use this sed:
sed -En '/^1/ {N;N;s/[[:blank:]]*Usd([^[:blank:]]+)[^\n]*$/\1/; s/\n/ /gp;}' file
1 Something here and else, 2000 edf, 60 pop 324.32
Or this awk would also work:
awk '$0 == 1 {
printf "%s", $0
getline
printf " %s ", $0
getline
sub(/Usd/, "")
print $1
}' file
1 Something here and else, 2000 edf, 60 pop 324.32
Pure Bash:
#! /bin/bash
exec <<EOF
*** *
more text with spaces and tabs
*****
1
Something here and else, 2000 edf, 60 pop
Usd324.32 2 Usd534.22
2
21st New tetx that will like to select with pattern, 334 pop
Usd162.14
*** *
more text with spaces and tabs, unicode
*****
EOF
while read -r line1; do
if [[ $line1 =~ ^1$ ]]; then
read -r line2
read -r line3col1 dontcare
printf '%s %s %s\n' "$line1" "$line2" "${line3col1#Usd}"
fi
done

Store output of lsblk command key=value into associative array in bash [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
> lsblk -Po mountpoint,label,uuid /dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Output from the lsblk example command:
MOUNTPOINT="/media/user/GParted Live" LABEL="GParted Live" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
I will be using above command in bash and I want to store the key values in partition associative array. The above lsblk output therefore needs to be processed and placed in an associative array
Like -
partition[MOUNTPOINT] should have /media/user/GParted Live
partition[LABEL] should have GParted Live
partition[UUID] should have xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Can someone please help by giving me a readable bash script?
Assumptions:
the lsblk output does not contain double quotes (") embedded in the 'value' strings
for this answer I'm going to place OP's lsblk output into a file (lsblk.out) and use said file as input to the proposed answer.
Sample input data:
$ cat lsblk.out
MOUNTPOINT="/media/user/GParted Live" LABEL="GParted Live" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
We'll start with a sed solution that uses a capture group to break the input into separate lines:
$ sed -E 's/([^ ][A-Z]*=\"[^\"]*\")[ $]/\1\n/g' lsblk.out
MOUNTPOINT="/media/user/GParted Live"
LABEL="GParted Live"
UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
We'll then feed this to a while loop to populate our array:
unset partition
declare -A partition
while IFS="=" read -r idx data
do
partition[${idx}]="${data}"
done < <(sed -E 's/([^ ][A-Z]*=\"[^\"]*\")[ $]/\1\n/g' lsblk.out)
We can then check the results of the operation :
$ typeset -p partition
declare -A partition=([UUID]="\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"" [LABEL]="\"GParted Live\"" [MOUNTPOINT]="\"/media/user/GParted Live\"" )
Alternatively:
for idx in "${!partition[#]}"
do
echo "${idx} : ${partition[${idx}]}"
done
Which generates:
UUID : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
LABEL : "GParted Live"
MOUNTPOINT : "/media/user/GParted Live"
If OP wants to strip out the double quotes ("):
while IFS="=" read -r idx data
do
partition[${idx}]="${data//\"/}" # strip out double quotes
done < <(sed -E 's/([^ ][A-Z]*=\"[^\"]*\")[ $]/\1\n/g' lsblk.out)
Then verify:
typeset -p partition
for idx in "${!partition[#]}"
do
echo "${idx} : ${partition[${idx}]}"
done
Which generates:
declare -A partition=([UUID]="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" [LABEL]="GParted Live" [MOUNTPOINT]="/media/user/GParted Live" )
UUID : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LABEL : GParted Live
MOUNTPOINT : /media/user/GParted Live
OP should be able to feed the lsblk command into the while loop like such:
while IFS="=" read -r idx data
do
partition[${idx}]="${data}"
# partition[${idx}]="${data//\"/}"
done < <(lsblk -Po mountpoint,label,uuid /dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | sed -E 's/([^ ][A-Z]*=\"[^\"]*\")[ $]/\1\n/g')
NOTE: to remove double quotes (") move the comment (#) up one line

Best way to alter a file in a bash script [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
i have a file that have variables and values
objective: open the file and replace all id by input id
[FILE]
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
and i can't replace the id values to new ones.
here's my code, any help or something will help. thanks
#!/bin/bash
filename=$1
uuid=$2
input="./$filename"
# awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval; print $1}' $input
while IFS= read -r line
do
awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval;}' $input
echo $line
done < "$input"
expected output
execute
./myscript.sh file.cnf 77
expected output:
[FILE]
var1 = 2
id = 77
var3 = 5
id = 77
var4 = 5
I think sed is the right tool for this. You can even use its -i switch and update the file in-place.
$ cat file.txt
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
$ NEW_ID=1234
$ sed -E "s/(id\s*=\s*)(.+)/\1${NEW_ID}/g" file.txt
var1 = 2
id = 1234
var3 = 5
id = 1234
var4 = 5
The string inside the quotes is a sed script for substituting some text with different text, and its general form is s/regexp/replacement/flags where "regexp" stands for "regular expression".
In the above example, the script looks for the string "id = ..." with any number of spaces or tabs around the "=" character. I divided the regexp into 2 groups (using parentheses) because we only want to replace the part to the right of the "=" character, and I don't think sed allows partial substitutions, so as a workaround I used \1 in the "replacement", which inserts the contents of the 1st group. The ${NEW_ID} actually gets evaluated by the shell so the value of the variable ("1234") is already part of the string by the time sed processes it. The g at the end stands for "global" and is probably redundant in this case. It makes sure that all occurrences of the regex on every line will get replaced; otherwise sed would only replace the first occurrence on each line.
Not sure. Bash scripts are extremely sensitive. I'm guessing your touch is what is causing this issue for a couple of reasons.
First whenever you touch a file name is should not consist of an operand or prefix unliss it is part of the shell script and $filename is shell or inline block quote. Touch is usually used for binaries or high priority data objects.
Second I'd try changing input and adjusted to $done and instead of echoing the $line echo the entire script using esac or end if instead of a do while loop.

inserting text into a specific line

I've got a text file, and using Bash I wish to insert text into into a specific line.
Text to be inserted for example is !comment: http://www.test.com into line 5
!aaaa
!bbbb
!cccc
!dddd
!eeee
!ffff
becomes,
!aaaa
!bbbb
!cccc
!dddd
!comment: http://www.test.com
!eeee
!ffff
sed '4a\
!comment: http://www.test.com' file.txt > result.txt
i inserts before the current line, a appends after the line.
you can use awk as well
$ awk 'NR==5{$0="!comment: http://www.test.com\n"$0}1' file
!aaaa
!bbbb
!cccc
!dddd
!comment: http://www.test.com
!eeee
!ffff
Using man 1 ed (which reads entire file into memory and performs in-place file editing without previous backup):
# cf. http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
line='!comment: http://www.test.com'
#printf '%s\n' H '/!eeee/i' "$line" . wq | ed -s file
printf '%s\n' H 5i "$line" . wq | ed -s file

Resources