0

I am trying to run a single command to change all .DS_Store files within a directory and all subdirectories recursively.

So far, I have been using the following commands to find the files, set their date modified to Jan 1 01:01:01 2001, and then to find them and set their permissions to read only for user, group, and everyone .

find . -name '.DS_Store' -print0 | xargs -0 sudo touch -t 200101010101
find . -name '.DS_Store' -print0 | xargs -0 sudo chmod 444

I'm guessing there is a more efficient way to do this with a single command, but I'm not sure how to use the output of the find command in more than a single following command.

Ideally, if there is any error/failure encountered with any of the files/commands, I would want to have the command line stop and output the error message rather than continuing to execute. I've seen elsewhere that using && to separate commands on a single line would do this, but am not sure how to use this in conjunction with the output from the find command.

This question is about a smaller part of a larger task I'm trying to accomplish. I've almost got it all working now except part of the script that only seems to work when I run it from terminal directly. Here's a link to the main issue in case anyone here might be able to help: Service to execute series of commands on selected folders to eliminate issues with .DS_Store files in Finder

MikMak
  • 2,107
  • 3
  • 14
  • 22
  • 1
    Do you want it to be more efficient in terms of typing (shorter command), or in terms of execution time (CPU/IO usage)? – u1686_grawity Jul 20 '22 at 13:46
  • Ideally, execution time. length of command not as important. thanks! – MikMak Jul 20 '22 at 17:22
  • Does macOS come with Python (or at least Perl) these days? Most methods based on find would either negate the benefit of 'xargs' (running two commands per file) or wouldn't have sufficient error checking. – u1686_grawity Jul 20 '22 at 19:06
  • I have no idea what "these days" you refer to specifically, but I'm a die-hard holdout still using macOS Sierra (10.12.6). Maybe that would help to answer that question? :) In case not, here's a ink to a screenshot of the relevant results found on my system using Find Any File. https://i.imgur.com/AiHD8Ed.png – MikMak Jul 21 '22 at 00:33

2 Answers2

2

You can do that by using the -exec option and using its output as argument for a subshell:

find . -name '.DS_Store' -exec zsh -c 'touch -t 200101010101 "$1" && sudo chmod 444 "$1" || echo "$1" >> errors.txt' zsh {} \;

As you can see I use zsh {} to pass an argument to zsh -c, which allows me to use the output of find via $1. Note that you need to use double quotes here, e.g. to avoid filenames containing spaces causing problems.

I use && to only continue with the next command, if the previous exited without an error.

If an error occurs, || allows me to run a command on failure, for example writing the filename into a errors.txt file.

mashuptwice
  • 3,244
  • @MikMak I am not aware of any difference in particular, but I've read about some arguments behaving different as well as some missing/different functionality. As I couldn't test this on a MacOS or BSD machine yet, I've added the note. – mashuptwice Jul 20 '22 at 18:34
  • @MikMak as you've accepted the answer, I suppose it works on MacOS without any problems. I will remove the note then. – mashuptwice Jul 21 '22 at 18:39
1

How about:

find . -name '.DS_Store' -exec sudo touch -t 200101010101 {} + -exec sudo chmod 444 {} + -print

(remember, -exec with + builds a single command with all files as arguments)

Paul
  • 1,128