3

I need to find all files that cointains in the first line the strings: "StockID" and "SellPrice".

Here is are some exemples of files :

1.csv :

StockID Dept    Cat2    Cat4    Cat5    Cat6    Cat1    Cat3    Title   Notes   Active  Weight  Sizestr Colorstr    Quantity    Newprice    StockCode   DateAdded   SellPrice   PhotoQuant  PhotoStatus Description stockcontrl Agerestricted
<blank> 1   0   0   0   0   22  0   RAF Air Crew Oxygen Connector   50801   1   150 <blank> <blank> 0   0   50866   2018-09-11 05:54:03 65  5   1   <br />\r\nA wartime RAF aircrew oxygen hose connector.<br />\r\n<br />\r\nAir Ministry stamped with Ref. No. 6D/482, Mk IVA.<br />\r\n<br />\r\nBrass spring loaded top bayonet fitting for the 'walk around' oxygen bottle extension hose (see last photo).<br />\r\n<br />\r\nIn a good condition.    2   0
<blank> 1   0   0   0   0   15  0   WW2 US Airforce Type Handheld Microphone    50619   1   300 <blank> <blank> 1   0   50691   2017-12-06 09:02:11 20  9   1   <br />\r\nWW2 US Airforce Handheld Microphone type NAF 213264-6 and sprung mounting Bracket No. 213264-2.<br />\r\n<br />\r\nType RS 38-A.<br />\r\n<br />\r\nMade by Telephonics Corp.<br />\r\n<br />\r\nIn a un-issued condition.    3   0
<blank> 1   0   0   0   0   22  0   RAF Seat Type Parachute Harness <blank> 1   4500    <blank> <blank> 1   0   50367   2016-11-04 12:02:26 155 8   1   <br />\r\nPost War RAF Pilot Seat Type Parachute Harness.<br />\r\n<br />\r\nThis Irvin manufactured harness is 'new old' stock and is unissued.<br />\r\n<br />\r\nThe label states Irvin Harness type C, Mk10, date 1976.<br />\r\nIt has Irvin marked buckles and complete harness straps all in 'mint' condition.<br />\r\n<br />\r\nFully working Irvin Quick Release Box and a canopy release Irvin  'D-Ring' Handle.<br />\r\n<br />\r\nThis harness is the same style type as the WW2 pattern seat type, and with some work could be made to look like one.<br />\r\n<br />\r\nIdeal for the re-enactor or collector (Not sold for parachuting).<br />\r\n<br />\r\nTotal weight of 4500 gms.   3   0

2.csv :

id  user_id organization_id hash    name    email   date    first_name  hear_about
1   2   15  <blank> Fairley teisjdaijdsaidja@domain.com 1129889679  John    0

I only want to find the files that contains on 1st line : "StockID" and "SellPrice" ; So in this exemple, i want to output only ./1.csv

I manage to do this, but i`m stuck now ;(

where=$(find "./backup -type f)
for x in $where; do
   head -1 $x | grep -w "StockID"
done
6

find + awk solution:

find ./backup -type f -exec \
awk 'NR == 1{ if (/StockID.*SellPrice/) print FILENAME; exit }' {} \;

In case if the order of crucial words may be different - replace pattern /StockID.*SellPrice/ with /StockID/ && /SellPrice/.


In case of huge number of files a more efficient alternative would be (processing a bunch of files at once; the total number of invocations of the command will be much less than the number of matched files):

find ./backup -type f -exec \
awk 'FNR == 1 && /StockID.*SellPrice/{ print FILENAME }{ nextfile }' {} +
  • 1
    i own you a coffee! it works. – Jonson Dec 9 '18 at 15:56
  • 1
    @user000001, see my update – RomanPerekhrest Dec 9 '18 at 18:26
  • how about making it more efficient with awk 'FNR == 1 &&/StockID.*SellPrice/ {print FILENAME}; {nextfile }' {} +. This will lead to fewer invocations of awk – iruvar Dec 9 '18 at 23:29
  • @iruvar, good hint. I've made an update. Thanks. – RomanPerekhrest Dec 10 '18 at 5:45
1

With GNU grep or compatible:

grep -Hrnm1 '^' ./backup | sed -n '/StockID.*SellPrice/s/:1:.*//p'

The recursive grep will print the first line of each file and print a filename:1:line without reading through the whole file (the -m1 flag should make it exit at the 1st match) and the sed will print the filename where the line part matches the pattern.

This will fail with file names which contain a :1: themselves or newline characters, but this is a risk worth taking instead of putting up some slow find + awk combo which executes another process for each file.

1

To avoid running one command per file and reading the entire files, with GNU awk:

(unset -v POSIXLY_CORRECT; exec find backup/ -type f -exec gawk '
  /\<StockID\>/ && /\<SellPrice\>/ {print FILENAME}; {nextfile}' {} +)

Or with zsh:

set -o rematchpcre # where we know for sure \b is supported
for file (backup/**/*(ND.)) {
  IFS= read -r line < $file &&
   [[ $line =~ "\bStockID\b" ]] &&
   [[ $line =~ "\bSellPrice\b" ]] &&
   print -r $file
}

Or:

set -o rematchpcre
print -rl backup/**/*(D.e:'
  IFS= read -r line < $REPLY &&
   [[ $line =~ "\bStockID\b" ]] &&
   [[ $line =~ "\bSellPrice\b" ]]':)

Or with bash on systems where native extended regular expressions support \<, \> word boundary operators (on others, you some others, you could also try [[:<:]]/[[:>:]] or \b instead):

RE1='\<StockId\>' RE2='\<SellPrice\>' find backup -type f -exec bash -c '
  for file do
    IFS= read -r line < "$file" &&
    [[ $line =~ $RE1 ]] &&
    [[ $line =~ $RE2 ]] &&
    printf "%s\n" "$file"
  done' bash {} +
  • Stéphane , I'm guessing you're going to have to account for $0, so the done' {} + should read something like done' bash {} + ? – iruvar Dec 11 '18 at 1:36
0

egrep + awk :

 egrep -Hrn 'StockID|SellPrice' ./backup | awk -F ':' '$2==1{print $1}'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.