Combine two text files in bash script - linux

I have a text file (FILE_A.txt) with this content:
Content1
Content2
Content3
And other text file (FILE_B.txt) with this content:
A[#]
B[#]
C[#]
I would like to combine FILE_A.txt and FILE_B.txt in other file (FILE_C.txt) in this way:
A[Content1]
B[Content2]
C[Content3]
How could I make this using bash shell in linux (sed, cut, grep, etc)?

Here we go.
# awk 'NR==FNR{a[NR]=$0;next;} sub(/#/,a[FNR])' FILE_A.txt FILE_B.txt
A[Content1]
B[Content2]
C[Content3]
How does this work?
NR==FNR - causes the following statements to be run if the record number matches the FILE record number - that is, we are currently reading only the firs tfile.
{a[NR]=$0;next;} - Store values from the first file in an array.
sub(/#/,a[FNR]) - Once we're in the second file, substitute # for the matching value stored from the first file. Note that this isn't inside curly brackets, so it's being evaluated as a condition. If the sub() statement succeeds, the current line is printed.

Use paste and sed as follows:
$ paste File_B.txt File_A.txt | sed 's/#]\s*\(.*$\)/\1]/g'
A[Content1]
B[Content2]
C[Content3]

The following reads both files concurrently, one line at a time, and store the lines in $value and $template. We then use bash's variable substring replacement to replace # within $template with the contents of $value.
exec 6<"FILE_B.txt" # open file for reading and assign file descriptor 6
while read -r value; do # loop through FILE_A.txt, storing each line as $value
read -r template <&6 # read a line from FILE_B.txt, store as $template
echo ${template/\#/$value} # replace value into the template in place of `#`
done <"FILE_A.txt"
exec 6<&- # close input file descriptor 6

Related

How to read a specific string from a file and save the whole line in another file in Bash Shell script

I have a file which contains many lines. Each line contains strings and numbers. My script should read the file and when a specific word is matched, it should save the complete line. For example one of the line contains: Computera abc23, when the script should find the word Computera in any line it should save the whole line i.e Computera abc23. The line should be saved in file1.
Similarly another line in the file contains word machine1, when the script should see the word machine1 it should save that line in file1
Here is my code:
#!/bin/sh
f="home/c/file.txt"
n=$(cat "$f")
echo "${n} >| /home/c/file1.txt"
My code reads and saves all the text from file to file1 But I would like to save only those lines where a specific word is matched
Something similar to this:
cat file | grep [pattern] | awk '{ print $number-of-column }' >> file1
I recommend reading about both cut and awk and how you can use them with grep command.

Shell Script With sed and Random number

How to make a shell script that receives one or more text files and removes from them whitespaces and blanklines. After that new files will have a random 2-digit number in front of them.
For example File1.txt generates File1_56.txt
Tried this:
#!/bin/bash
for file in "$*"; do
sed -e '/^$/d;s/[[:blank:]]//g' $* >> "$*_$$.txt"
done
But when I give 2 files as input script merges them into one single file, when I want for each file a separate one.
Try:
#!/bin/bash
for file in "$#"; do
sed -e '/^$/d;s/[[:blank:]]//g' "$file" >> "${file%.txt}_$$.txt"
done
Notes
To loop over each argument without word splitting or other hazards, use for file in "$#" not for file in "$*"
To run the sed command on one file instead of all, specify "$file" as the file, not $*.
To save the output to the correct file, use "${file%.txt}_$$.txt" where ${file%.txt} is an example of suffix removal: it removes the final .txt from the file name.
$$ is the process ID. The title says mentions a "random" number. If you want a random number, replace $$ with $RANDOM.

How to insert a text in middle of nth line of file

I am trying to insert a pipe symbol in between nth line of a file. Like the one present in the output of diff command.
Do not want to use VI editor.
For example,desired line is 2nd line of the file:
cat filename
Hi Hi
Hello Hello
This This
is Is
it it
desired output:
cat filename
Hi Hi
Hello | Hello
This This
is Is
it it
For your own sanity, just use awk:
$ awk 'NR==2{mid=length($0)/2; $0=substr($0,1,mid) "|" substr($0,mid+1)} 1' file
Hi Hi
Hello | Hello
This This
is Is
it it
To modify the original file you can add > tmp && mv tmp file or use -i inplace if you have GNU awk.
You basically cannot modify in place some textual file by inserting a character inside a line. You need to read all its content, modify that content in memory, then write the new content.
You might be interested in GNU ed, which can edit a file programmatically (inside some script).
You could use awk (or any other filter) and redirect its output to some temporary file, then rename that temporary file as the original one.
sed '
# select 2nd line
/2/ {
# keep original in memory
G;h
:divide
# cycle by taking the 2 char at the egde of string (still string end by the \n here) and copy only first to other part of the separator (\n)
s/\(.\)\(.*\)\(.\)\(\n.*\)/\2\4\1/
# still possible, do it again
t divide
# add last single char if any and remove separator
s/\(.*\)\n\(.*\)/\2\1/
# add original string (with a new line between)
G
# take first half string and end of second string, and place a pipe in the middle in place of other char
s/\(.*\)\n\1\(.*\)/\1|\2/
}' YourFile
posix sed, so --POSIXfor GNU sed
Sel explain

How do I insert the results of several commands on a file as part of my sed stream?

I use DJing software on linux (xwax) which uses a 'scanning' script (visible here) that compiles all the music files available to the software and outputs a string which contains a path to the filename and then the title of the mp3. For example, if it scans path-to-mp3/Artist - Test.mp3, it will spit out a string like so:
path-to-mp3/Artist - Test.mp3[tab]Artist - Test
I have tagged all my mp3s with BPM information via the id3v2 tool and have a commandline method for extracting that information as follows:
id3v2 -l name-of-mp3.mp3 | grep TBPM | cut -D: -f2
That spits out JUST the numerical BPM to me. What I'd like to do is prepend the BPM number from the above command as part of the xwax scanning script, but I'm not sure how to insert that command in the midst of the script. What I'd want it to generate is:
path-to-mp3/Artist - Test.mp3[tab][bpm]Artist - Test
Any ideas?
It's not clear to me where in that script you want to insert the BPM number, but the idea is this:
To embed the output of one command into the arguments of another, you can use the "command substitution" notation `...` or $(...). For example, this:
rm $(echo abcd)
runs the command echo abcd and substitutes its output (abcd) into the overall command; so that's equivalent to just rm abcd. It will remove the file named abcd.
The above doesn't work inside single-quotes. If you want, you can just put it outside quotes, as I did in the above example; but it's generally safer to put it inside double-quotes (so as to prevent some unwanted postprocessing). Either of these:
rm "$(echo abcd)"
rm "a$(echo bc)d"
will remove the file named abcd.
In your case, you need to embed the command substitution into the middle of an argument that's mostly single-quoted. You can do that by simply putting the single-quoted strings and double-quoted strings right next to each other with no space in between, so that Bash will combine them into a single argument. (This also works with unquoted strings.) For example, either of these:
rm a"$(echo bc)"d
rm 'a'"$(echo bc)"'d'
will remove the file named abcd.
Edited to add: O.K., I think I understand what you're trying to do. You have a command that either (1) outputs out all the files in a specified directory (and any subdirectories and so on), one per line, or (2) outputs the contents of a file, where the contents of that file is a list of files, one per line. So in either case, it's outputting a list of files, one per line. And you're piping that list into this command:
sed -n '
{
# /[<num>[.]] <artist> - <title>.ext
s:/\([0-9]\+.\? \+\)\?\([^/]*\) \+- \+\([^/]*\)\.[A-Z0-9]*$:\0\t\2\t\3:pi
t
# /<artist> - <album>[/(Disc|Side) <name>]/[<ABnum>[.]] <title>.ext
s:/\([^/]*\) \+- \+\([^/]*\)\(/\(disc\|side\) [0-9A-Z][^/]*\)\?/\([A-H]\?[A0-9]\?[0-9].\? \+\)\?\([^/]*\)\.[A-Z0-9]*$:\0\t\1\t\6:pi
t
# /[<ABnum>[.]] <name>.ext
s:/\([A-H]\?[A0-9]\?[0-9].\? \+\)\?\([^/]*\)\.[A-Z0-9]*$:\0\t\t\2:pi
}
'
which runs a sed script over that list. What you want is for all of the replacement-strings to change from \0\t... to \0\tBPM\t..., where BPM is the BPM number computed from your command. Right? And you need to compute that BPM number separately for each file, so instead of relying on seds implicit line-by-line looping, you need to handle the looping yourself, and process one line at a time. Right?
So, you should change the above command to this:
while read -r LINE ; do # loop over the lines, saving each one as "$LINE"
BPM=$(id3v2 -l "$LINE" | grep TBPM | cut -D: -f2) # save BPM as "$BPM"
sed -n '
{
# /[<num>[.]] <artist> - <title>.ext
s:/\([0-9]\+.\? \+\)\?\([^/]*\) \+- \+\([^/]*\)\.[A-Z0-9]*$:\0\t'"$BPM"'\t\2\t\3:pi
t
# /<artist> - <album>[/(Disc|Side) <name>]/[<ABnum>[.]] <title>.ext
s:/\([^/]*\) \+- \+\([^/]*\)\(/\(disc\|side\) [0-9A-Z][^/]*\)\?/\([A-H]\?[A0-9]\?[0-9].\? \+\)\?\([^/]*\)\.[A-Z0-9]*$:\0\t'"$BPM"'\t\1\t\6:pi
t
# /[<ABnum>[.]] <name>.ext
s:/\([A-H]\?[A0-9]\?[0-9].\? \+\)\?\([^/]*\)\.[A-Z0-9]*$:\0\t'"$BPM"'\t\t\2:pi
}
' <<<"$LINE" # take $LINE as input, rather than reading more lines
done
(where the only change to the sed script itself was to insert '"$BPM"'\t in a few places to switch from single-quoting to double-quoting, then insert the BPM, then switch back to single-quoting and add a tab).

Bash script: Appending text at the last character of specific line of a file

I am trying to append a variable at the last character of a specific line of a file from a bash script.
The file is called myfile.txt and what I want to do is to append the contents of a variable named VERSION right after the last character of the line of the file that contains the unique string MYVERSION.
That is, if in this line there is the following:
MYVERSION=0.1
and VERSION="-custom_P1" then, I want to have the following:
MYVERSION=0.1-custom_P1
Thank you all for the help.
Try this:
sed -i "/^MYVERSION=/ s/\$/$VERSION/" myfile.txt
The idea is that it finds a line that starts with MYVERSION= and then replaces the end of that line with the contents of the $VERSION environment variable.
Edit: originally I wasn't sure if the first $ needed to be escaped, but #sehe's comment and its upvoters convinced me that it does.
Try
sed -e "s/^MYVERSION=/MYVERSION=.*$/&${VERSION}/g" < myfile.txt
The command appends the value of VERSION to the line with 'MYVERSION='

Resources