Reputation: 166282
I want to copy executable files from one directory to another.
The source directory includes all sorts of files I don't care about (build artifacts). I want to grab only the executable files, using a bash script that works on both OS X and Linux.
By executable, I mean a file that has the executable permission, and would pass test -x $filename
.
I know I can write some python script but then I would be introducing a dependency on python (just to copy files!) which is something I really want to avoid.
Note: I've seem a couple of similar questions but the answers seem to only work on Linux (as the questions specifically asked about Linux). Please do not mark this as duplicate unless the duplicate question is indeed about cross platform copying of executable files only.
Upvotes: 2
Views: 3302
Reputation: 439607
Your own answer is conceptually elegant, but slow, because it creates at least one child process for every input file (test
), plus an additional one for each matching file (cp
).
Here's a more efficient bash
alternative that:
cp
.#!/usr/bin/env bash
exeFiles=()
for f in "$src_dir"/*; do [[ -x $f && -f $f ]] && exeFiles+=( "$f" ); done
cp "${exeFiles[@]}" "$dest_dir/"
exeFiles=()
initializes the array in which to store the matching filenames.for f in "$src_dir"/*
loops over all files and directories located directly in $scr_dir
; note how *
must be unquoted for globbing (filename expansion) to occur.
[[ -x $f && -f $f ]]
determines whether the item at hand is executable (-x
) and a (regular) file -f
; note that double-quoting variable references inside [[ ... ]]
is (mostly) optional.exeFiles+=( "$f" )
appends a new element to the array"${exeFiles[@]}"
refers to the resulting array as a whole and robustly expands to the array elements as individual arguments - see Bash arrays.Upvotes: 3
Reputation: 166282
After some experimentation, this seems to work on both OS X and Ubuntu
find "$src_dir" -maxdepth 1 -type f -exec test -x {} \; -exec cp {} "$dest_dir/" \;
Note that the -maxdepth 1
is specific to my use case where I don't care about recursively going through all the directories.
-type f
is necessary because directories also count as executables
I pass two -exec
flags. The exec flag not only executes the command but also counts as a filter so that if the command returns a non-zero exit code, the file is filtered out.
The way to use exec is write out whatever command you want, use {}
to supply the current file, and terminate with \;
The first -exec
returns a success exit code only if the file is executable.
The second -exec
performs the copy, but it's not executed if the first -exec
fails.
Upvotes: 3