6

I have a control file - cntl.txt

2
3
5

Data file - data.txt

red
blue
yellow
green
violet
orange

I need to reading the lines matching from control file, here the output expected is:

blue
yellow 
violet

5 Answers 5

5

Example of a very inefficient solution:

for i in $(<control.txt); do awk -v c=$i 'NR~c{ print $0 }' data.txt; done;

I report also a good solution I learned tonight:

awk 'FNR==NR{ z[$0]++;next }; FNR in z' control.txt data.txt
4
  • What @don_crissti said is true. Also see Why is using a shell loop to process text considered bad practice?
    – Wildcard
    Nov 2, 2016 at 22:11
  • @don_crissti You're right. I knew it's very inefficient, but unfortunately it's the best that I thought. I still have a lot to learn, I really appreciate your help!
    – andreatsh
    Nov 2, 2016 at 22:28
  • Your awk solution is certainly worth an upvote imho (especially since the OP tagged awk) Nov 2, 2016 at 23:14
  • 1
    note that z[$0] alone will do... no need of ++...
    – Sundeep
    Jun 20, 2017 at 14:44
4

Using only POSIX specified features of Sed:

sed -n -e "$(sed '/./s/$/p/' cntl.txt)" data.txt

Of course if your cntl.txt file has lines besides numbers, you may get an error. But if it has empty lines these will be handled correctly (i.e. they will not affect the output).

3
  • This does not work if you have 10+ lines, as join expects a lexicographically sorted data.
    – zeppelin
    Nov 2, 2016 at 22:04
  • @zeppelin, you are quite correct. I've upvoted your answer and have now removed the incorrect portion of this answer.
    – Wildcard
    Nov 2, 2016 at 22:06
  • I've upvoted your updated answer.
    – zeppelin
    Nov 2, 2016 at 22:07
3

Try this:

join <(nl data.txt|sort -k1b,1) <(cat cntl.txt|sort -k1b,1) | sort -nk1,1 | cut -d' ' -f2-

nl - will enumerate lines for you

 1  red
 2  blue
 3  yellow
 4  green
 5  violet
 6  orange

| sort -k1b,1 - will sort them by the line number (first field), lexicographically

cat cntl.txt| sort -k1b,1 - will sort the control file in the same order

2
3
5

join <() <() - will join the sorted (and numbered) "data" with the sorted "control", on the first field (i.e. line number)

2 blue
3 yellow
5 violet

|sort -nk1,1 - will re-sort the results numerically (to put the lines back in order)

| cut -d' ' -f2- - will drop the line number field

blue
yellow
violet
2
  • 1
    > That seems silly That is not silly, as join operates on the lexicographically and not numerically sorted data.
    – zeppelin
    Nov 2, 2016 at 21:58
  • > Also, <(...) is a Bash-ism, not specified by POSIX Yep, this is probably not canonicaly POSIX, but it is not a bash-only extension either, at least ksh and zsh do support this too.
    – zeppelin
    Nov 2, 2016 at 22:03
2

With sed only:

sed -n "$(sed -e 's/$/p;/' < cntl.txt)" data.txt
1
  • 1
    Good, but no reason to use tr. Newlines are a perfectly acceptable command separator for Sed. (Also, $(...) is the preferred syntax for command substitution.)
    – Wildcard
    Nov 2, 2016 at 22:10
-1

Another possible solution:

IFS=$'\n' read -d '' -r -a colors < 'data.txt'; unset IFS;

for i in $(<cntl.txt); do
        echo ${colors[i-1]} 
done

IFS line sets up internal file separator as newline and inserts each line from data.txt into array. After that you loop through lines in cntl.txt and print array elements with given index from it (minus 1 because you start your data.txt from 1, not from 0, otherwise it would be unnecessary).

1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .