I have a file where the first line looks something like this:
"Window Size" "Param 1" Result Time
So it basically has some words separated by spaces. I need a Linux command that will read the word at a specific position, where words enclosed in quatation marks should be seen as one word (and returned without the quatation marks).
So if I execute the command with the number 1 (doesn't matter if it starts from 0), the following should be returned:
Window Size
If executed with 3, it should return:
Result
Any idea how to do this, with awk maybe?
Let's say the name of your file containing your data is test.txt. One possible solution in BASH with arrays:
eval "E=( $(cat test.txt) )"
echo ${E[0]}
echo ${E[2]}
The output is:
Window Size
Result
Related
I have the following Bash script which loops through the lines of a file:
INFO_FILE=playlist-info-test.txt
line_count=$(wc -l $INFO_FILE | awk '{print $1}')
for ((i=1; i<=$line_count; i++))
do
current_line=$(sed "${i}q;d" $INFO_FILE)
CURRENT_PLAYLIST_ORIG="$current_line"
input_file="$CURRENT_PLAYLIST_ORIG.mp3"
echo $input_file
done
This is a sample of the playlist-info-test.txt file:
Playlist 1
Playlist2
Playlist 3
The output of the script should be as follows:
Playlist 1.mp3
Playlist2.mp3
Playlist 3.mp3
However, I am getting the following output:
.mp3list 1
.mp3list2
.mp3list 3
I have spent a few hours on this and can't understand why the ".mp3" part is being moved to the front of the string. I initially thought it was because of the space in the lines of the input file, but removing the space doesn't make a difference. I also tried using a while loop with read line and the input file redirected into it, but that does not make any difference either.
I copied the playlist-info-test.txt contents and the script, and get the output you expected. Most likely there are non-printable characters in your playlist-info-test.txt or script which are messing up the processing. Check the binary contents of both files using for example xxd -g 1 and look for non-newline (0a) non-printing characters.
Did the file come from Windows? DOS and Windows end their lines with carriage return (hex 0d, sometimes represented as \r) followed by linefeed (hex 0a, sometimes represented as \n). Unix just uses linefeed, and so tends to treat the carriage return as part of the content of the line. In your case, it winds up at the end of the current_line variable, so input_file winds up something like "Playlist 1\r.mp3". When you print this to the terminal, the carriage return makes it go back to the beginning of the line (that's what carriage return means), so it prints as:
Playlist 1
.mp3
...with the ".mp3" printed over the "Play" part, rather than on the next line like I have it above.
Solution: either fix the file (there's a fairly standard dos2unix program that does precisely this), or change your script to strip carriage returns as it reads the file. Actually, I'd recommend a rewrite anyway, since your current use of sed to pick out lines is rather weird and inefficient. In a shell script, the standard way to read through a file line-by-line is to use a loop like while read -r current_line; do [commands here]; done <"$INFO_FILE". There's a possible problem that if any commands inside the loop read from standard input, they'll wind up inhaling part of that file; you can fix that by passing the file over unit 3 rather than standard input. With that fix and a trick to trim carriage returns, here's what it looks like:
INFO_FILE=playlist-info-test.txt
while IFS=$' \t\n\r' read -r current_line <&3; do
CURRENT_PLAYLIST_ORIG="$current_line"
input_file="$CURRENT_PLAYLIST_ORIG.mp3"
echo "$input_file"
done 3<"$INFO_FILE"
(The carriage return trim is done by read -- it always auto-trims leading and trailing whitespace, and setting IFS to $' \t\n\r' tells it to treat spaces, tabs, linefeeds, and carriage returns as whitespace. And since that assignment is a prefix to the read command, it applies only to that one command and you don't have to set IFS back to normal afterward.)
A couple of other recommendations while I'm here: double-quote all variable references (as I did with echo "$input_file" above), and avoid all-caps variable names (there are a bunch with special meanings, and if you accidentally use one of those it can have weird effects). Oh, and try passing your scripts to shellcheck.net -- it's good at spotting common mistakes.
What I am doing is to encode a character 13 places from its current location.
For example, if I input welcome, it should echo jrypbzr.
this is what I wrote:
read words
echo $words | tr '[A-Za-z]' '[????]' (Please ignore the ???? part.)
This successfully solved the encoding problem, however, I need to input multiple times and the code I wrote only read one time. Can someone tell me how to input multiple times?
Thanks!
First, have your input in a text file. Then
while read words
do
# here, do whatever you want with words
done < your-input-file.txt
Explanation: you feed contents of the input file to the while loop, which reads it line by line and stores in words.
If you want to use a delimiter other than newline, you can use:
while IFS=";" read words
and place within the IFS= " " whatever delimiter you like.
I am just wondering what is the best way to add multiple lines within a file. I.e. I want to replace a tag within a file i.e. /#tag
with multiple lines, say 3
echo "line 1"
echo "line 2"
echo "line 3"
I know I can read each line of the file and if I encounter the tag could pipe the new lines to the file, however, due to the size of the file, that takes way too long.
I am sure there must be a better way?
Just use sed:
sed -i 's:/#tag:line 1\nline 2\nline 3:' file
The s command is simply 'substitute', usually the separator would be a / but since this is in the tag we can use : instead. See http://www.grymoire.com/unix/sed.html if you have never used sed before. The /#tag can be a regular expression, just as with grep.
I have thousand of files in a directory and each file contains numbers of defined variables starting with keyword DEFINE and ending with a semicolon (;), I want to copy all the occurrences of the data between this keyword(Inclusive) into a target file.
Example: Below is the content of the text file:
/* This code is for lookup */
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
END.
Now from the above content i just want to copy the section starting with DEFINE and ending with ; into a target file i.e. the output should be:
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
this needs to done for thousands of scripts and multiple occurences, Please help out.
Thanks a lot , the provided code works, but to a limited extent only when the whole sentence is in a single line but the data is not supposed to be in one single line it is spread in multiple line like below:
/* This code is for lookup */
DEFINE variable as a1 expr= if branchno > 55
then
extract (n123f1 using brach, code)
else
branchno = null
;
END.
The code is also in the above fashion i need to capture all the data between DEFINE and semicolon (;) after every define there will be an ending semicolon ;, this is the pattern.
It sounds like you want grep(1):
grep '^DEFINE.*;$' input > output
Try using grep. Let's say you have files with extension .txt in present directory,
grep -ho 'DEFINE.*;' *.txt > outfile
Output:
DEFINE variable as a1 expr= extract (n123f1 using brach, code);
Short Description
-o will give you only matching string rather than whole line, if line also contains something else and want to ommit it.
-h will suppress file names before matching result
Read man page of grep by typing man grep on your terminal
EDIT
If you want capability to search in multiple lines, you can use pcregrep with -M option
pcregrep -M 'DEFINE.*?(\n|.)*?;' *.txt > outfile
Works fine on my system. Check man pcregrep for more details
Reference : SO Question
One can make a simple solution using sed with version :
sed -n -e '/^DEFINE/{:a p;/;$/!{n;ba}}' your-file
Option -n prevents sed from printing every line; then each time a line begins with DEFINE, print the line (command p) then enter a loop: until you find a line ending with ;, grab the next line and loop to the print command. When exiting the loop, you do nothing.
It looks a bit dirty; it seems that the version sed15 has a shorter (and more straightforward) way to achieve this in one line:
sed -n -e '/^DEFINE/,/;$/p' your-file
Indeed, only for this version of sed, both patterns are treated; for other versions of sed like mine under cygwin, the range patterns must be on separate lines to work properly.
One last thing to remember: it does not treat inclusive patterned ranges, i.e. it stops printing after the first encountered end-pattern even if multiple start patterns have been matched. Prefer something with awk if this is a feature you are looking for.
I am running find command to get list of files having a particular size and then i save the output in a file, now i traverse through this file one by one and ask user which one he wants to delete. I wanted to do something like add a number next to each file in list so that user can directly enter the number associated with this file and delete instead of having to go over the whole file. Please help.
select f in $(find . -name '*.txt'); do
if [ -n "$f" ]; then
# put your command here
echo "rm $f"
fi
done
find . -size 5k -okdir rm {} ";"
asks you for each file, whether to perform the action or not, without an intermediate file.
The -okdir is a Gnu-extension to find, and not available for all implementations.
Another, lean approach is to use select:
select fno in $(find . -size 5k);
do
echo rm $fno
done
which is a bashism, maybe not present in your shell.
help select shows its usage. Unfortunately, it doesn't, like the find-solution too, allow to select multiple entries at once, but you get repeated opportunity to select something, until you hit Ctrl+D, which is quiet comfortable.
select: select NAME [in WORDS ... ;] do COMMANDS; done
Select words from a list and execute commands.
The WORDS are expanded, generating a list of words. The
set of expanded words is printed on the standard error, each
preceded by a number. If `in WORDS' is not present, `in "$#"'
is assumed. The PS3 prompt is then displayed and a line read
from the standard input. If the line consists of the number
corresponding to one of the displayed words, then NAME is set
to that word. If the line is empty, WORDS and the prompt are
redisplayed. If EOF is read, the command completes. Any other
value read causes NAME to be set to null. The line read is saved
in the variable REPLY. COMMANDS are executed after each selection
until a break command is executed.
Exit Status:
Returns the status of the last command executed.
This is what it looks like:
select fno in *scala ; do echo "fno: " $fno; done
1) Cartesian.scala 6) MWzufall.scala
2) Haeufigkeit.scala 7) Shuffle.scala
3) HelloChain.scala 8) eHWCChain.scala
4) Lychrel.scala 9) equilibrum.scala
5) M.scala 10) scala
#? 3
fno: HelloChain.scala
#? 3 4
fno:
#?
Note that words are separated by spaces, so you have to take care in the second example, if you have to work with whitespace in filenames.