I've read a few sample codes but I just don't understand how to use the foreach-like loop to iterate through the file collection. Here is how I get the list of files:
ls=:0 dir '*.*'
I can echo this out and it works fine. But what I need to do is process each file 1 at a time using some kind of loop. so something like:
ls=:0 dir '*.*'
foreach (file in ls) {
do something(file)
do something else.. so on
}
in the manuals a for each loop would look like this:
smoutput each i.10
Clearly that's no use, it prints 1 to 10, I don't know how to modify this to work with my directory listing.
The each keyword is for a single-line loop. There is a for loop, which you can use inside a defined function (or verb).
Example:
myFunction =: 3 : 0
ls =. 1 dir '*.*'
for_file. ls do.
doSomething ;file
doSomethingElse ;file
end.
)
The for_. variant is arguably the most useful. The name of your variable file goes right between the underscore and the period. The important thing to remember is that the for loop will give each item as it appears in the array, so if ls is an array of boxed strings, each file will show up as a single boxed string.
You probably need a list of this form:
ls =: 1 dir '*.*'
which is a list of files not a list of characters like 0 dir '*.*'.
You can use each on this list, e.g.:
]ls =: 1 dir '*'
┌──────────┬──────────────┬──────────┬───────────┬───────┐
│build_defs│build_jconsole│build_libj│build_tsdll│jconfig│
└──────────┴──────────────┴──────────┴───────────┴───────┘
;# each ls NB. number of characters for each filename
10 14 10 11 7
Related
i just want to know how to do this take a directory as an argument and then prints the longest entry in that directory as an output.
Any Ideas ?
find "$1" -maxdepth 1 -type f | awk '{ fils[$0]=length($0) } END { PROCINFO["sorted_in"]="#val_num_asc";for ( i in fils ) { largfil=i } print largfil }'
With find piped through to awk, first search a given directory ($1 signifying a passed argument) for files and then through awk, create an array fils indexed with the file names and with the length of the filenames as the value. In the end block, set the sort order accordingly and then loop through the array setting a variable largfil to the filename from the array. We then print largfil at the end of the loop, signifying the filename with the largest character size.
I have 500 wave files in a folder ABC which are named like
F1001
F1002
F1003
...
F1100
F2001
F2002
...
F2100
M3001
M3002
...
M3100
M4001
M4002
...
M4100
all with extension .wav.
Also I have a text file which contains 3 digit numbers like
001
003
098
034 .... (200 in total).
I want to select wave files from the folder ABC whose names end with these 3 digits.
Expecting MATLAB or bash script solutions.
I read this:
Copy or move files to another directory based on partial names in a text file. But I don't know how to use it for me.
for Matlab
1) Get all the file names in the folder using functions dir or rdir.
2) Using for loop go through every filename and add the last 3 digits of every filename to an array (array A). You will need str2num() here
3) Parse all 3 digit numbers to an array (array B)
4) Using function ismember(B, A) find which elements of B are contained in A
5) Load corresponding filenames
find . -name "*.wav" | grep -f <(awk '{print $0 ".wav"}' file)
grep -f will use the patterns stored in file, one per line, and look for them in your find result. But you want the three numbers to be at the end, so in the above command the last awk statement will provide a modified file with ".wav" appended at each line. So for the line 001, "0001.wav" will match but any file 0010.wav will not.
see: process substitution syntax and grep --help
function wavelist()
wavefiles=dir('*.wav'); % loaded wave files
myfolder='/home/adspr/Desktop/exps_sree/waves/selectedfiles'; %Output folder to store files
for i=1: numel(wavefiles) %for each wave file
filename=wavefiles(i).name;
[~,name,~] = fileparts(filename); % found the name of file without extension
a=name(end-2:end); %get the last 3 digits in the file name
fileID = fopen('nameslist','r');
while ~feof(fileID)
b=fgetl(fileID); % get each line in the list file
if strcmp(a,b) % compare
movefile(filename,myfolder); % moved to otput folder
end
end
fclose(fileID);
end
end
I don't think this is a simple answer, thats why I asked here. Anyway, my problem solved, thats why I posted this as an answer.
Thank you all.
I have a series of image files with filename formats as follows.
9407-C-9406-B-038.jpg
9407-C-9406-B-118.jpg
9422-AC-012.jpg
9422-AC-112.jpg
9422-BD-043.jpg
9422-BD-Still-001.jpg
9405_M.jpg
9792A.jpg
9792B.jpg
The relevant portion for my purpose is the first 4 characters, the rest is irrelevant.
I'd like to rename the files such that the leading 4 char string is retained, the extension is retained, but the rest is incremented when a duplicate is encountered :
e.g :
9422-AC-012.jpg becomes 942200.jpg
9422-AC-112.jpg becomes 942201.jpg
9422-BD-043.jpg becomes 942202.jpg
9422-BD-Still-001.jpg becomes 942203.jpg
9405_M.jpg becomes 940500.jpg
9792A.jpg becomes 979200.jpg
9792B.jpg becomes 979201.jpg
Using 'rename' I can strip the string after the first 4 chars, and I can increment in totality, which results with the incremental portion of the filename ending up in the thousands.
rename -n -N 0001 's/(?<=.{4}).*/$N.jpg/' *.jpg
Can anyone suggest a way to strip the filename after the first 4 chars, rename the file, and increment only when a duplicate is encountered?
awk to the rescue!
With the filenames listed in file
$ while read f t;
do
echo "mv $f $t";
done < <(awk -F\. '{k=substr($0,1,4); printf "%s\t%s%04d.%s\n", $0,k,a[k]++,$NF}' file)
mv 9407-C-9406-B-038.jpg 94070000.jpg
mv 9407-C-9406-B-118.jpg 94070001.jpg
mv 9422-AC-012.jpg 94220000.jpg
mv 9422-AC-112.jpg 94220001.jpg
mv 9422-BD-043.jpg 94220002.jpg
mv 9422-BD-Still-001.jpg 94220003.jpg
mv 9405_M.jpg 94050000.jpg
mv 9792A.jpg 97920000.jpg
mv 9792B.jpg 97920001.jpg
remove echo for the actual renaming, assumes no white space in the file name, at least 4 chars long and has extension.
the example shows two digit counter, you mentioned the counter to be in the thousands, here it's controlled by the print format %04d, change 4 to 2 if you don't expect more than 100. Regardless, the counter will create unique file but may extend from the allocated number of digits (e.g. it will generate 00,01,..99, and for the next one will be 100.)
I'm conducting a reiterative analysis and having to submit more than 5000 jobs to a batch system on a large computer cluster.
I'm wanting to run a bash for loop but call both the current list item and the next item in my script. I'm not sure the best way to do this using this format:
#! /bin/bash
for i in `cat list.txt`;
do
# run a bunch of code on $i (ex. Data file for 'Apples')
# compare input of $i with next in list {$i+1} (ex. Compare 'Apples' to 'Oranges', save output)
# take output of this comparison and use it as an input for the next analysis of $i (ex. analyze 'Apples' some more, save output for the next step, analyze data on 'Oranges')
# save this output as the input for next script which analyses the next item in the list {$i+1} (Analysis of 'Oranges' with input data from 'Apples', and comparing to 'Grapes' in the middle of the loop, etc., etc.)
done
Would it be easiest for me to provide a tabular input list in a while loop? I would really prefer not to do this as I would have to do some code editing, albeit minor.
Thanks for helping a novice -- I've looked all over the interwebs and ran through a bunch of books and haven't found a good way to do this.
EDIT: For some reason I was thinking there might have been a for loop trick to do this but I guess not; it's probably easier for me to do a while loop with a tabular input. I was prepared to do this, but I didn't want to re-write the code I had already.
UPDATE: Thank you all so much for your time and input! Greatly appreciated.
Another solution is to use bash arrays. For example, given a file list.txt with content:
1
2
3
4
4
5
You can create an array variable with the lines of the file as elements as:
$ myarray=(1 2 3 4 4 5)
While you could also do myarray=( $(echo list.txt) ) this may split on whitespace and handle other output inappropriately, the better method is:
$ IFS=$'\n' read -r -d '' -a myarray < list.txt
Then you can access elements as:
$ echo "${myarray[2]}"
3
To length of the array is given by ${#myarray[#]}. A list of all indices is given by ${!myarray[#]}, and you can loop over this list of indices:
for i in "${!myarray[#]}"; do
echo "${myarray[$i]} ${myarray[$(( $i + 1))]}"
done
Output:
1 2
2 3
3 4
4 4
4 5
5
While there are likely simpler solutions to your particular use case, this would allow you to access arbitrary combinations of array elements in the loop.
This answer assumes that you want your values to overlap -- meaning that a value given as next then becomes curr on the following iteration.
Assuming you encapsulate your code in a function that takes two arguments (current and next) when a next item exists, or one argument when on the last item:
# a "higher-order function"; it takes another function as its argument
# and calls that argument with current/next input pairs.
invokeWithNext() {
local funcName=$1
local curr next
read -r curr
while read -r next; do
"$funcName" "$curr" "$next"
curr=$next
done
"$funcName" "$curr"
}
# replace this with your own logic
yourProcess() {
local curr=$1 next=$2
if (( $# > 1 )); then
printf 'Current value is %q, and next item is %q\n' "$curr" "$next"
else
printf 'Current value is %q; no next item exists\n' "$curr"
fi
}
These definitions done, you can run:
invokeWithNext yourProcess <list.txt
...yield output such as:
Current value is 1, and next item is 2
Current value is 2, and next item is 3
Current value is 3, and next item is 4
Current value is 4, and next item is 5
Current value is 5; no next item exists
$ printf '%d\n' {0..10} | paste - -
0 1
2 3
4 5
6 7
8 9
10
So if you just want to interpolate lines so that you can read two variables per line...
while read -r odd even; do
…
done < <(paste - - < inputfile)
You will need to do additional work if your lines contain whitespace.
I would replace the for loop with a while read xx loop.
Something along the lines of
cat list.txt | while read line; do
if read nextline; then
# You have $line and $nextline
else
# You have garbage in $nextline and the last line of list.txt in $line
fi
done
I have following two lines in my Input file.
Sample Input file ( name of file - file.txt)
String1 value 'string2'
..
..
..
Call string1
Desired output :
File.txt. ( i.e. Name of file )
Basically i want names of file - if it contains these two lines
1) string1 value 'string2'
2) call string1
1) and 2) above are two different lines and there could be many lines in between.
P.S. a) i am searching for 'string2' . 'string1' could be any 8 characters. I do not know 'string1'
b) 'string2' will be always in single quote (')
c) 'string2' will always be preceded by string 'value'
Thanks
Try this...
gc input.txt |?{$_ -match '.*value ''(.*)'''}|%{$matches[1]}
The below script goes through each file in the current directory and looks for the files with the 2 lines in the content. Not sure if this meets your situation but it is not difficult to customize it to you environment.
dir *.* | foreach {if (get-content $_| Select-String -pattern "^[A-Za-z]{8} value 'string2'","Call string1") {"$_"}}
The output (in my lab) is:
C:\Documents\ManualScripts\test.txt
test.txt is the file I created for testing this script. The content is as following:
abcdefgh value 'string2'
hello world
I love church
I love Jesus
call string1
Alibaba, sesame open the door!
To #dazedandconfused I believe your script return "String1" which is part of the second line instead of file name he/she requested. Besides your script doesn't reflect another one of his/her needs: "'string1' could be any 8 characters". Forgive me if I am wrong.