0

I have 2 files that I needed to grep in a separate file.

The two files are in this directory /var/list

TB.1234.txt
TB.135325.txt

I have to grep them in another file in another directory which is in /var/sup/. I used the command below:

for i in TB.*; do grep "$i" /var/sup/logs.txt; done

what I want to do is, if the result of the grep command contains the word "ERROR" the files which is found in /var/list will be moved to another directory /var/last.

for example I grep this file TB.1234.txt to /var/sup/logs.txt then the result is like this:

ERROR: TB.1234.txt

TB.1234.txt will be move to /var/last.

please help. I don't know how to construct the logic on how to move the files, I'm stuck in that I provided, I am also trying to use two greps in a for loop but I am encountering an error.

I am new in coding and really appreciates any help and suggestions. Thank you so much.

Jiggen
  • 1
  • 2
  • Where do you `grep` for `ERROR` in your command? And where do you move files? – Renaud Pacalet Oct 27 '22 at 05:16
  • Hi @RenaudPacalet, I want to move the files when there is a word 'ERROR' in the result from the grep above. it will be moved to /var/last. thank you – Jiggen Oct 27 '22 at 06:05
  • Yes, but what you show does not search for `ERROR` and does not move any file. You should first try to fix that. – Renaud Pacalet Oct 27 '22 at 06:07
  • Hi @RenaudPacalet that's what I'm trying to figure out. that's why I asked the question. I couldn't construct the logic since I am new in coding. I have now the result, I just want to know how to move the files after the grep command when the result has the word 'ERROR' on it. – Jiggen Oct 27 '22 at 06:22
  • What's the role of `/var/sup/logs.txt` then? – tripleee Oct 27 '22 at 08:03
  • Hi @tripleee, that is the log file where I want to grep the files. it contains the filename which is TB.12314.txt then ERROR and SUCCESS words in it. – Jiggen Oct 27 '22 at 08:09

2 Answers2

2

If you are asking how to move files which contain "ERROR", this should be extremely straightforward.

for file in TB.*; do
    grep -q 'ERROR' "$file" &&
    mv "$file" /var/last/
done

The notation this && that is a convenient shorthand for

if this; then
    that
fi

The -q option to grep says to not print the matches, and quit as soon as you find one. Like all well-defined commands, grep sets its exit code to reflect whether it succeeded (the status is visible in $?, but usually you would not examine it directly; perhaps see also Why is testing ”$?” to see if a command succeeded or not, an anti-pattern?)


Your question is rather unclear, but if you want to find either of the matching files in a third file, perhaps something like

awk 'FNR==1 && (++n < ARGC-1) { a[n] = FILENAME; nextfile }
  /ERROR/ { for(j=1; j<=n; ++j) if ($0 ~ a[j]) b[a[j]]++ }
  END { for(f in b) print f }' TB*.txt /var/sup/logs.txt |
xargs -r mv -t /var/last/

This is somewhat inefficient in that it will read all the lines in the log file, and brittle in that it will only handle file names which do not contain newlines. (The latter restriction is probably unimportant here, as you are looking for file names which occur on the same line as the string "ERROR" in the first place.)

In some more detail, the Awk script collects the wildcard matches into the array a, then processes all lines in the last file, looking for ones with "ERROR" in them. On these lines, it checks if any of the file names in a are also found, and if so, also adds them to b. When all lines have been processed, print the entries in b, which are then piped to a simple shell command to move them.

xargs is a neat command to read some arguments from standard input, and run another command with those arguments added to its command line. The -r option says to not run the other command if there are no arguments.

(mv -t is a GNU extension; it's convenient, but not crucial to have here. If you need portable code, you could replace xargs with a simple while read -r loop.)

The FNR==1 condition requires that the input files are non-empty.

If the text file is small, or you expect a match near its beginning most of the time, perhaps just live with grepping it multiple times:

for file in TB.*; do
    grep -Eq "ERROR.*$file|$file.*ERROR" /var/sup/logs.txt &&
    mv "$file" /var/last/
done

Notice how we now need double quotes, not single, around the regular expression so that the variable $file gets substituted in the string.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I really appreciate it. last one question, what if I did not get any result from the grep? but decided to move the file also? should I just use the same code? thank you – Jiggen Oct 27 '22 at 09:35
  • I don't understand what that means. If you want to move the file unconditionally, remove the grep. If you want to move only if `grep` fails, switch `&&` to `||` – tripleee Oct 27 '22 at 09:38
  • Hi, sorry for the late revert, I did accomplished the unconditional moving of files, but I had encountered this problem, I want to exclude the word "Error" from the grep command, grep -v is not working for me I don't know why. I used this one: for file in TB.*; do grep $file | grep -v "Error" – Jiggen Nov 02 '22 at 14:54
  • That seems to be missing a file name entirely. You are grepping for `$file` on standard input (with a quoting error; that should be `grep "$file"` if that's what you want) and then excluding `"Error"`. To find lines which match `"$file"` but not match ERROR in `/var/sup/logs.txt` the command would be `grep "$file" /var/sup/logs.txt | grep -v "ERROR"` (probably use `grep -F` to make it run more quickly and avoid spurious matches if the file name contains regex metacharacters). – tripleee Nov 02 '22 at 15:18
  • Hi triplee, I have now successfully grep and omit the word error in my script. but I have encountered another problem, whenever I used the first one which is grepping the word Error and the filename, I have noticed that I have a result that looks like this: "[sb]sb01> grep TB.123456 /var/sup/logs.txt Error: TB.123456 TB.123456" [sb]sb01> grep TB.123 /var/sup/logs.txt Error: TB.123 – Jiggen Nov 05 '22 at 13:08
  • since I am grepping the word Error I am now also getting the one that has another line in it.(The first one, which means it is a successful file) I don't need the first one. I just need to move the file that has a single line that matches this one "Error: TB.123" any suggestions on how could I do it? been trying to play with the code but I still getting the two. – Jiggen Nov 05 '22 at 13:09
  • Don't pile on new questions in comments. I don't understand exactly what you are trying to ask; but all of this sounds like you should take a basic course in shell scripting; these are not hard problems to solve once you understand the basics. – tripleee Nov 05 '22 at 17:28
  • basically, I grepped two different files but have almost the same contents. the only difference is the first one has another line in it. the other one contains **Error: TB.123456** while the other one contains **Error: TB.123456 TB.123456**. there are no uniqueness between the two results except for the extra line. I just need to move the only file that has one line in the result. I am stuck since grep -P is not working as well as grep -w. – Jiggen Nov 06 '22 at 05:49
  • (Accept this answer if it solved your problem, and) ask a new question with proper details. – tripleee Nov 06 '22 at 06:52
0

grep has an -l switch, showing only the filename of the file which contains a pattern. It should not be too difficult to write something like (this is pseudocode, it won't work, it's just for giving you an idea):

if $(grep -l "ERROR" <directory> | wc -l) > 0
then foreach (f in $(grep -l "ERROR")
     do cp f <destination>
end if

The wc -l is to check if there are any files which contain the word "ERROR". If not, nothing needs to be done.

Edit after Tripleee's comment:

My proposal can be simplified as:

if grep -lq "ERROR" TB.*;
then foreach (f in $(grep -l "ERROR")
     do cp f <destination>
end if

Edit after Tripleee's second comment:

This is even shorter:

for f in $(grep -l "ERROR" TB.*);
do cp "$f" destination;
done
Dominique
  • 16,450
  • 15
  • 56
  • 112
  • 1
    That's a [useless use of `wc -l`](https://www.iki.fi/era/unix/award.html#wc); you simply want `if grep -lq "ERROR" TB.*; then` (and probably also add an `-F` option). – tripleee Nov 02 '22 at 15:20
  • @tripleee: thanks for your proposal, I edited my answer accordingly. I didn't mention the `-F` switch, as I don't see a reason for avoiding regular expressions in this particular case. – Dominique Nov 02 '22 at 15:27
  • 1
    There are no regex metacharacters in `"ERROR"` so it's just a minor optimization. – tripleee Nov 02 '22 at 15:28
  • The `if` is unnecessary because `for` will loop zero times if there are no matches. (I left a comment about this earlier but I removed it because it covered some other stuff too.) – tripleee Nov 03 '22 at 03:54
  • @tripleee: what will be the final script then? (Feel free to edit my answer) – Dominique Nov 03 '22 at 08:29
  • `for f in $(grep -l "ERROR" TB.*); do cp "$f" destination; done` will work for file names which do not contain whitespace or other shell metacharacters; but probably review https://mywiki.wooledge.org/BashFAQ/020 – tripleee Nov 03 '22 at 08:36
  • @tripleee: thanks. I edited my answer accordingly. – Dominique Nov 03 '22 at 08:45