Bash

How do you run a command eg chmod for each line of a file

19 September 2026 · 16 min read

How do you run a command eg chmod for each line of a file

Have you ever faced the tedious task of modifying permissions, perhaps using chmod, across a large number of files listed in a text file? Manually running commands line by line is not only time-consuming but also prone to errors. Fortunately, there are efficient ways to automate this process, enabling you to run a command, eg chmod, for each line of a file using shell scripting. This article will guide you through various methods to accomplish this task, ensuring accuracy and saving you valuable time. We’ll explore different approaches using xargs, while loops, and awk, providing practical examples and explaining the nuances of each technique. Mastering these methods will significantly enhance your command-line proficiency and streamline your file management workflows, allowing you to apply changes across multiple files swiftly and effectively. This capability is invaluable for system administrators, developers, and anyone managing large datasets.

Understanding the Challenge: Applying Commands to Multiple Files

The challenge of applying a command to each line of a file often arises when dealing with file permissions, bulk renaming, or any operation that needs to be applied uniformly across a set of files. Consider a scenario where you have a file named filelist.txt containing a list of filenames, and you need to change the permissions of each file to 755 using the chmod command. Without automation, you would have to manually type chmod 755 filename for each entry in filelist.txt. This is not only impractical for large lists but also increases the risk of errors. Shell scripting provides powerful tools to automate this process, ensuring that each file is processed correctly and efficiently. The key is to iterate through the file, extract each filename, and execute the desired command on it.

Several tools and techniques can be used to achieve this automation. The xargs command is particularly useful for building and executing command lines from standard input. while loops, combined with read, offer a more flexible approach, allowing you to perform complex operations on each line. awk, a powerful text-processing tool, can also be used to extract filenames and execute commands. Each method has its advantages and disadvantages, depending on the complexity of the task and the specific requirements of the environment. Understanding these different approaches empowers you to choose the most suitable solution for your specific needs, ensuring efficient and accurate execution of commands on multiple files.

For example, imagine a web server migration where you need to update the ownership of thousands of files. Instead of manually changing the owner for each file, you could create a script that reads the list of files from a text file and applies the chown command automatically. This not only saves time but also reduces the likelihood of human error, which can be critical in a production environment. By understanding the various techniques available, you can tailor your approach to the specific requirements of the task, whether it’s a simple permission change or a more complex operation involving multiple commands and conditions.

Using xargs to Execute Commands on File Lists

The xargs command is a powerful tool for building and executing command lines from standard input. It reads items from standard input, delimited by blanks or newlines, and uses them as arguments to a specified command. This makes it particularly well-suited for processing lists of filenames. To run a command, eg chmod, for each line of a file using xargs, you can pipe the contents of the file to xargs along with the desired command and its arguments. This approach is often more efficient than using loops, especially for large lists of files, as xargs can execute commands in parallel, leveraging multiple CPU cores to speed up the process.

Here’s a practical example of using xargs to change the permissions of files listed in filelist.txt to 755: cat filelist.txt | xargs chmod 755. This command reads the contents of filelist.txt, and for each filename, it executes the command chmod 755 filename. xargs automatically handles the splitting of filenames and passing them as arguments to chmod. This is a concise and efficient way to apply the same command to multiple files. According to the GNU xargs documentation [GNU Findutils], xargs is designed to handle large numbers of arguments efficiently, making it suitable for processing files with thousands of entries.

One important consideration when using xargs is handling filenames with spaces or special characters. By default, xargs splits arguments based on whitespace. To handle filenames with spaces, you can use the -0 option in conjunction with the find command or other tools that output null-terminated filenames. For example, if you’re generating the list of files using find, you can use find . -print0 | xargs -0 chmod 755. This ensures that filenames with spaces are treated as single arguments, preventing errors. Understanding these nuances is crucial for using xargs effectively and avoiding common pitfalls when dealing with complex filenames.

Employing while Loops and read for Iterative Processing

Another common method to run a command, eg chmod, for each line of a file is using a while loop in conjunction with the read command. This approach offers more flexibility compared to xargs, allowing you to perform more complex operations on each line. The read command reads a line from standard input and assigns it to a variable, which can then be used in the loop to execute the desired command. This method is particularly useful when you need to perform conditional operations or manipulate the filename before executing the command. While loops, however, are generally slower than xargs, especially for very large lists of files, as they execute commands sequentially rather than in parallel.

Here’s an example of using a while loop to change the permissions of files listed in filelist.txt to 755:

 while read filename; do chmod 755 "$filename" done < filelist.txt 

This script reads each line from filelist.txt and assigns it to the filename variable. Inside the loop, the chmod command is executed with the filename variable as an argument. The double quotes around $filename are crucial for handling filenames with spaces or special characters. This ensures that the filename is treated as a single argument, preventing errors. According to a Stack Overflow discussion [Stack Overflow - Looping through a file], this is a common and reliable method for processing files line by line in Bash scripts. The while loop approach also allows you to perform more complex operations on each line. For example, you could add a condition to only change the permissions of files that are not already executable:

 while read filename; do if [ ! -x "$filename" ]; then chmod 755 "$filename" fi done < filelist.txt 

This script checks if the file is not executable (! -x “$filename”) before changing its permissions. This level of control is not easily achievable with xargs. The ability to add conditional statements and perform more complex logic makes the while loop a versatile tool for processing files line by line. Leveraging awk for Advanced Text Processing and Command Execution

awk is a powerful text-processing tool that can also be used to run a command, eg chmod, for each line of a file. It’s particularly useful when you need to perform more complex text manipulation before executing the command. awk reads input line by line, splits each line into fields based on a specified delimiter (by default, whitespace), and allows you to perform actions on each field. This makes it suitable for extracting filenames from structured text files or performing operations based on specific patterns. While awk might have a steeper learning curve compared to xargs or while loops, its capabilities for advanced text processing make it a valuable tool in your scripting arsenal.

Here’s an example of using awk to change the permissions of files listed in filelist.txt to 755: awk '{system("chmod 755 \"" $1 "\"")}' filelist.txt. This command reads each line from filelist.txt, and for each line, it executes the chmod 755 command with the first field ($1) as the filename. The system() function in awk allows you to execute shell commands. The double quotes around \" are necessary to escape the quotes within the awk command. This approach is concise and efficient, especially for simple operations. According to the GNU awk documentation [GNU Awk User’s Guide], the system() function provides a powerful way to interact with the operating system from within awk scripts.

One of the advantages of using awk is its ability to perform more complex text manipulation before executing the command. For example, if your filelist.txt contains filenames along with other information, you can use awk to extract only the filenames and then execute the command. Consider a file where each line contains a filename followed by its size: filename.txt 12345. You can use the following awk command to change the permissions of the files: awk '{print $1}' filelist.txt | xargs chmod 755. This command first uses awk to print the first field (the filename) and then pipes the output to xargs, which executes the chmod command. This demonstrates the flexibility of awk in handling structured text files and extracting the necessary information for command execution.

Best Practices and Considerations

When working with scripts that run a command, eg chmod, for each line of a file, it’s crucial to follow best practices to ensure accuracy, security, and efficiency. Always test your scripts thoroughly before running them on production data. Start with a small subset of files to verify that the script is working as expected. Use verbose output or logging to track the progress of the script and identify any errors. This helps in debugging and ensures that all files are processed correctly. According to a study by the SANS Institute [SANS Institute] regarding scripting best practices, comprehensive testing and logging are essential for preventing errors and ensuring security.

Consider the security implications of your scripts. Avoid running scripts with elevated privileges unless absolutely necessary. If you need to run a script as root, use sudo with caution and ensure that the script is well-tested and secure. Be careful when handling filenames with spaces or special characters. Always use appropriate quoting and escaping techniques to prevent command injection vulnerabilities. For example, using “$filename” in a while loop or -0 with xargs can help prevent issues. Additionally, always validate the input to your scripts to prevent malicious users from injecting harmful commands.

Finally, optimize your scripts for performance. Use xargs for large lists of files, as it can execute commands in parallel. Avoid unnecessary operations within loops. Use efficient text-processing tools like awk or sed for complex text manipulation. Monitor the performance of your scripts and identify any bottlenecks. By following these best practices, you can ensure that your scripts are accurate, secure, and efficient, saving you time and preventing errors. Remember, thorough testing and validation are key to successful scripting.

  • Always test your scripts thoroughly before running them on production data.
  • Use verbose output or logging to track the progress of the script and identify any errors.
  1. Create a text file containing the list of filenames (e.g., filelist.txt).
  2. Choose the appropriate method (xargs, while loop, or awk) based on the complexity of the task.
  3. Write the script using the chosen method, ensuring proper quoting and escaping.
  4. Test the script on a small subset of files.
  5. Run the script on the entire list of files.

FAQ Section

What is the best method for processing a large list of files?
For large lists of files, `xargs` is generally the most efficient method due to its ability to execute commands in parallel.
How do I handle filenames with spaces or special characters?
Use appropriate quoting and escaping techniques, such as `"$filename"` in a `while` loop or `-0` with `xargs`.
Can I perform conditional operations on each file?
Yes, using a `while` loop allows you to add conditional **Question & Answer :** For example, right now I'm using the following to change a couple of files whose Unix paths I wrote to a file:
cat file.txt | while read in; do chmod 755 "$in"; done 

Is there a more elegant, safer way?

Read a file line by line and execute commands: 4+ answers

Because the main usage of shell ( and others shells like bash) is to run other commands, there is not only 1 answer!!

  1. Shell command line expansion
  2. xargs dedicated tool
  3. while read with some remarks and consideration about parallel processing
  4. while read -u using dedicated fd, for interactive processing (sample)
  5. running shell with inline generated script

Regarding the OP request: running chmod on all targets listed in file, xargs is the indicated tool. But for some other applications, small amount of files, etc…

  1. Read entire file as command line argument.

If

  • your file is not too big (tested on my host with 128Mb file, with more than 10'000'000 lines) and
  • all files are well named (without spaces or other special chars like quotes),

you could use shell command line expansion. Simply:

chmod 755 $(<file.txt) 

This command is the simplier one.

  1. xargs is the right tool

For

  • bigger amount of files, or almost any number of lines in your input file…
  • files holding names that could contain spaces or special characters

For many binutils tools, like chown, chmod, rm, cp -t

xargs chmod 755 <file.txt 

Could be used after a pipe on found files by find:

find /some/path -type f -uid 1234 -print | xargs chmod 755 

If you have special chars and/or a lot of lines in file.txt.

xargs -0 chmod 755 < <(tr \\n \\0 <file.txt) find /some/path -type f -uid 1234 -print0 | xargs -0 chmod 755 

If your command need to be run exactly 1 time for each entry:

xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt) 

This is not needed for this sample, as chmod accepts multiple files as arguments, but this matches the title of question.

For some special cases, you could even define the location of the file argument in commands generated by xargs:

xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt) 

Test with seq 1 5 as input

Try this:

xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5) 
Blah 1 blabla 1.. Blah 2 blabla 2.. Blah 3 blabla 3.. Blah 4 blabla 4.. Blah 5 blabla 5.. 

where your command is executed once per line.

IMPORTANT PREAMBLE BEFORE CHAPTER 3.

Doing loop under shell is generally a bad idea! There is a lot of warning about doing loop under shell!

Before doing loop, think parallelisation and dedicated tools!!

You could use bash for interact with and administrate dedicated tools. Some samples:

  1. while read and variants.

For this, make sure to end the file with a newline character.

As OP suggests,

cat file.txt | while read in; do chmod 755 "$in" done 

will work, but there are 2 issues:

  • cat | is a useless fork, and
  • | while ... ;done will become a subshell whose environment will disappear after ;done.

So this could be better written:

while read in; do chmod 755 "$in" done < file.txt 

But

  • You may be warned about $IFS and read flags:

help read

read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered... 

In some cases, you may need to use

while IFS= read -r in;do chmod 755 "$in" done <file.txt 

for avoiding problems with strange filenames. And maybe if you encounter problems with UTF-8:

while LANG=C IFS= read -r in ; do chmod 755 "$in" done <file.txt 

While you use a redirection from standard inputfor reading file.txt`, your script cannot read other input interactively (you cannot use standard input for other input anymore).

3.1 while read for limited number of concurrent parallel tasks

If you plan to run a big number of repetitive tasks, using multiprocessing, you could do something like:

maxProc=4 ... wait4oneTask() { wait -np epid results[epid]=$? ... unset "running[$epid]" } ... while read file ;do ... exec {shC_Fd}>"${tmpLoc}_${file//\//_}" shellcheck -f gcc "$file" >&$shC_Fd 2>&1 & lpid=$! ... running[lpid]='' ((${#running[@]}>=maxProc)) && wait4oneTask done while ((${#running[@]})); do wait4oneTask done 

This is extracted from Parallel ShellCheck script: parShellCheck.sh a sample of parallelizing process and overall statistics of collected shellcheck remarks.

  1. while read, using dedicated fd.

Syntax: while read ...;done <file.txt will redirect standard input to come from file.txt. That means you won’t be able to deal with processes until they finish.

This will let you use more than one input simultaneously, you could merge two files (like here: scriptReplay.sh), or maybe:

You plan to create an interactive tool, you have to avoid use of standard input and use some alternative file descriptor.

Constant file descriptors are:

  • 0 for standard input
  • 1 for standard output
  • 2 for standard error.

4.1 posix shell first

You could see them by:

ls -l /dev/fd/ 

or

ls -l /proc/$$/fd/ 

From there, you have to choose unused numbers between 0 and 63 (more, in fact, depending on sysctl superuser tool) as your file descriptor.

For this demo, I will use file descriptor 7:

while read <&7 filename; do ans= while [ -z "$ans" ]; do read -p "Process file '$filename' (y/n)? " foo [ "$foo" ] && [ -z "${foo#[yn]}" ] && ans=$foo || echo '??' done if [ "$ans" = "y" ]; then echo Yes echo "Processing '$filename'." else echo No fi done 7<file.txt 

If you want to read your input file in more differents steps, you have to use:

exec 7<file.txt # Without spaces between `7` and `<`! # ls -l /dev/fd/ read <&7 headLine while read <&7 filename; do case "$filename" in *'----' ) break ;; # break loop when line end with four dashes. esac .... done read <&7 lastLine exec 7<&- # This will close file descriptor 7. # ls -l /dev/fd/ 

4.2 Same under bash

Under bash, you could let him choose any free fd for you and store into a variable:
exec {varname}</path/to/input:

while read -ru ${fle} filename;do ans= while [ -z "$ans" ]; do read -rp "Process file '$filename' (y/n)? " -sn 1 foo [ "$foo" ] && [ -z "${foo/[yn]}" ] && ans=$foo || echo '??' done if [ "$ans" = "y" ]; then echo Yes echo "Processing '$filename'." else echo No fi done {fle}<file.txt 

Or

exec {fle}<file.txt # ls -l /dev/fd/ read -ru ${fle} headline while read -ru ${fle} filename;do [[ -n "$filename" ]] && [[ -z ${filename//*----} ]] && break .... done read -ru ${fle} lastLine exec {fle}<&- # ls -l /dev/fd/ 

5. filtering input file for creating shell commands

sed <file.txt 's/.*/chmod 755 "&"/' | sh 

This won’t optimise forks, but this could be usefull for more complex (or conditional) operation:

sed <file.txt 's/.*/if [ -e "&" ];then chmod 755 "&";fi/' | sh sed 's/.*/[ -f "&" ] \&\& echo "Processing: \\"&\\"" \&\& chmod 755 "&"/' \ file.txt | sh 

This can be very useful if sed input is a feed instead of a file. Practical sample: Using rsync log output as sed input for deleting corresponding description file when a project file are deleted. See my answer to Remove file if a file with the same name but different extension doesn’t exist in another directory which differ a lot from what SO asker did expect.