106

Scenario :

A folder in Linux system. I want to loop through every .xls file in a folder.

This folder typically consists of various folders, various filetypes (.sh, .pl,.csv,...).

All I want to do is loop through all files in the root and execute a program only on .xls files.

Edit :

The problem is the program I have to execute is 'xls2csv' to convert from .xls to .csv format. So, for each .xls file I have to grab the filename and append it to .csv.

For instance, I have a test.xls file and the arguments fro xls2csv are : xls2csv test.xls test.csv

Did I make sense?

4 Answers 4

229

bash:

for f in *.xls ; do xls2csv "$f" "${f%.xls}.csv" ; done
Sign up to request clarification or add additional context in comments.

6 Comments

This is exactly what I was looking for for a completely different reason. A little editing, and it’s perfect for my specific need. Thanks =D And for anyone who isn’t clear on what this does: ${f%.ext} substitutes the filename without the extension, so in this example, it’d render to "filename.csv" rather than "filename.xls.csv".
Heh I Googled "linux foreach txt file" and found this. This was exactly what I needed, I was actually trying to use xls2csv but figured I wouldn't find any results searching for that :)
$f will have a value of *.xls if the *.xls doesn't match any file. Check my answer if that's a problem.
Or you could just set nullglob in order to skip the loop.
Works great! I use it with 7-zip to archive each file separately when necessary. You can also set up this logic in combination with the solution here if you use this function often.
|
22
for i in *.xls ; do 
  [[ -f "$i" ]] || continue
  xls2csv "$i" "${i%.xls}.csv"
done

The first line in the do checks if the "matching" file really exists, because in case nothing matches in your for, the do will be executed with "*.xls" as $i. This could be horrible for your xls2csv.

Comments

17

Look at the find command.

What you are looking for is something like

find . -name "*.xls" -type f -exec program 

Post edit

find . -name "*.xls" -type f -exec xls2csv '{}' '{}'.csv;

will execute xls2csv file.xls file.xls.csv

Closer to what you want.

2 Comments

find -maxdepth 1 to exclude subfolders. This also converts test.xls to test.xls.csv instead of test.csv. So not quite what OP asked for, but pretty close.
Using {} as a substring of an argument rather than an entire argument is a GNU extension, not guaranteed by the POSIX spec for find. As such, this answer isn't necessarily portable to non-GNU platforms.
6
find . -type f -name "*.xls" -printf "xls2csv %p %p.csv\n" | bash

bash 4 (recursive)

shopt -s globstar
for xls in /path/**/*.xls
do
  xls2csv "$xls" "${xls%.xls}.csv"
done

2 Comments

Excellent solution. I've used this to allow me to write haml/scss and build into html/css at design-time for a node.js project.
This is outright dangerous if you don't trust your filenames not to contain things like $(rm -rf /)... or just whitespace.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.