Related
Right now, I am working on a "Text Editor" made with Bash. Everything was going perfectly until I tested it. When I opened the file the script created, everything was jumbled up. I eventually figured out it had something to do with the cat BASHTE/* >> $file I had put in. I still have no idea why this happens. My crappy original code is below:
#!/bin/bash
# ripoff vim
clear
echo "###############################################################################"
echo "# BASHTE TEXT EDITOR - \\\ = interupt :q = quit :w = write #"
echo "# :wq = Write and quit :q! = quit and discard :dd = Delete Previous line #"
echo "###############################################################################"
echo ""
read -p "Enter file name: " file
touch .$file
mkdir BASHTE
clear
echo "###############################################################################"
echo "# BASHTE TEXT EDITOR - \\\ = interupt :q = quit :w = write #"
echo "# :wq = Write and quit :q! = quit and discard :dd = Delete Previous line #"
echo "###############################################################################"
while true
do
read -p "$lines >" store
if [ "$store" = "\\:q" ]
then
break
elif [ "$store" = "\\:w" ]
then
cat BASHTE/* >> $file
elif [ "$store" = "\\:wq" ]
then
cat BASHTE/* >> $file
rm -rf .$file
break
elif [ "$store" = "\\:q!" ]
then
rm -rf BASHTE
rm -rf $file
break
elif [ "$store" = "\\:dd" ]
then
LinesMinusOne=$(expr $lines - 1)
rm -rf BASHTE/$LinesMinusOne.txt
else
echo $store >> BASHTE/$lines.txt
# counts the number of times the while loop is run
((lines++))
fi
done
This is what I got after I typed in the alphabet:
b
j
k
l
m
n
o
p
q
r
s
c
t
u
v
w
x
y
z
d
e
f
g
h
I
This was what I inputted
a
v
c
d
e
f
g
h
I
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
\\:wq
Any help would be great, Thanks
BASHTE/* is in lexical order, so every line starting with 1 will come before every line starting with 2 and so on. That means the order of your input lines is:
1 a
10 j
11 k
12 l
...and so on...
To make the lines sort well with the * operator, you'll need to name them with leading zeros, for example:
# ...
echo $store >> BASHTE/$(printf %020d $lines).txt
# ...
I chose the %020d format because it should store any number of lines applicable for a 64-bit system, since 2 ** 64 = 18446744073709551616, which is 20 digits long.
I have a script where I read in columns of data from an "input file", then use those to change some variables in another file.
Here is my script, titled FA_grid_changer.sh
#!/bin/bash
# before runnning, please ensure your inlist has the following parameters: RSP_mass, RSP_Teff, RSP_L, RSP_X, RSP_Z,
# log_directory, photo_directory, RSP_alfa, photo_interval, profile_interval, history_interval, terminal_interval,
# max_num_profile_models, RSP_max_num_periods
export OMP_NUM_THREADS=8
# get current directory
dir=$PWD
data_file="$dir"/input.dat
mesa_inlist="$dir"/inlist_rsp_RR_Lyrae
# set STR to 1 for saving history and profile files BEFORE the model has reached full-amplitude pulsations
# set STR to 2 for saving history and profile files AFTER the model has reached full-amplitude pulsations
STR=1
max_num_periods=4000
max_num_periods_2=$(($max_num_periods + 4))
if [[ "$STR" = 1 ]]
then
echo Configuring inlist to save settings before full-amplitude pulsations...
sed -i \
-e "s/^\([[:blank:]]*photo_interval\).*/\1 = 1000/i" \
-e "s/^\([[:blank:]]*profile_interval\).*/\1 = 1000/i" \
-e "s/^\([[:blank:]]*history_interval\).*/\1 = 1000/i" \
-e "s/^\([[:blank:]]*terminal_interval\).*/\1 = 4000/i" \
-e "s/^\([[:blank:]]*max_num_profile_models\).*/\1 = 100/i" \
-e "s/^\([[:blank:]]*RSP_max_num_periods\).*/\1 = $max_num_periods/i" \
"$mesa_inlist"
sleep 1.5
while read -ra fields; do
echo Changing model parameters...
sleep 0.5
sed -i \
-e "s/^\([[:blank:]]*RSP_mass\).*/\1 = ${fields[3]}/i" \
-e "s/^\([[:blank:]]*RSP_Teff\).*/\1 = ${fields[5]}/i" \
-e "s/^\([[:blank:]]*RSP_L\).*/\1 = ${fields[4]}/i" \
-e "s/^\([[:blank:]]*RSP_X\).*/\1 = ${fields[2]}/i" \
-e "s/^\([[:blank:]]*RSP_Z\).*/\1 = ${fields[1]}/i" \
-e "s/^\([[:blank:]]*log_directory\).*/\1 = 'LOGS\/LOGS_${fields[0]}'/i" \
-e "s/^\([[:blank:]]*photo_directory\).*/\1 = 'photos\/photos_${fields[0]}'/i" \
#-e "s/^\([[:blank:]]*RSP_alfa\).*/\1 = ${fields[6]}/i" \
"$mesa_inlist"
echo Compiling MESA...
sleep 0.5
./mk
echo Running MESA...
sleep 0.5
./rn
done < "$data_file"
elif [[ "$STR" = 2 ]]
then
echo Configuring inlist to save settings after full-amplitude pulsations...
sed -i \
-e "s/^\([[:blank:]]*photo_interval\).*/\1 = 1000/i" \
-e "s/^\([[:blank:]]*profile_interval\).*/\1 = 1/i" \
-e "s/^\([[:blank:]]*history_interval\).*/\1 = 1/i" \
-e "s/^\([[:blank:]]*terminal_interval\).*/\1 = 4000/i" \
-e "s/^\([[:blank:]]*max_num_profile_models\).*/\1 = -1/i" \
-e "s/^\([[:blank:]]*RSP_max_num_periods\).*/\1 = $max_num_periods_2/i" \
"$mesa_inlist"
sleep 1.5
else
echo STR\: Not an acceptable option. Please \set STR to 1 or 2.
fi
Here is my input file input.dat, which I am keeping in the same directory.
RRL1 0.001 0.749 0.61036165206037 48.3930329458203 6795.10018238295
RRL2 0.001 0.749 0.60627327846453 59.8125833648911 6793.11236483182
RRL3 0.001 0.749 0.606272337551755 56.5141274900899 7059.10474568471
And here is the file I am trying to change inlist_rsp_RR_Lyrae
! long-period RR Lyrae star: M=0.65Ms, L=60Ls, Teff=6500K, X=0.75, Z=0.0004
! original test case contributed by Radek Smolec.
&star_job
!pgstar_flag = .true.
/ ! end of star_job namelist
&controls
! check for retries and backups as part of test_suite
!max_number_retries = -1
!max_number_backups = 0
!max_model_number = 15000
! RSP controls
! x_integer_ctrl(1) = 10 ! which period to check
x_ctrl(1) = 0.71262d0 ! expected period (in days)
RSP_mass = 0.606272337551755
RSP_Teff = 7059.10474568471
!RSP_L = 59.3141274900899
RSP_L = 56.5141274900899
RSP_X = 0.749
RSP_Z = 0.001
RSP_max_num_periods = 4000
RSP_nmodes = 10 ! number of modes LINA will calculate for initial model
RSP_nz = 150 ! number of zones of model
log_directory = 'LOGS/LOGS_M081_second'
photo_directory = 'photos/photos_M081_second'
! output controls
!num_trace_history_values = 2
trace_history_value_name(1) = 'rel_E_err'
trace_history_value_name(2) = 'log_rel_run_E_err'
! for cases in which you have a run that has reached steady pulses
! and you want to look at plots of just a few periods to see the details,
! i suggest the following method. interrupt the run soon after
! it makes a photo. remove or delete LOGS/history.data to force
! creation of a new one when restart. edit the inlist to set
! history_interval to 1 to get maximum time resolution.
! restart the run and let it go for as many periods as desired.
do_history_file = .true.
photo_interval = 1000
profile_interval = 1000
history_interval = 1000
terminal_interval = 4000
max_num_profile_models = 100
!photo_interval = 1
!profile_interval = 1
!history_interval = 1
!terminal_interval = 1
! FOR DEBUGGING
!RSP_report_undercorrections = .true.
!report_hydro_solver_progress = .true. ! set true to see info about newton iterations
!report_ierr = .true. ! if true, produce terminal output when have some internal error
!stop_for_bad_nums = .true.
!trace_evolve = .true.
! hydro debugging
!hydro_check_everything = .true.
!hydro_inspectB_flag = .true.
!hydro_sizequ_flag = .true.
! for making logs, uncomment hydro_dump_call_number plus the following
! to make residual logs, uncomment hydro_sizequ_flag
! to make correction logs, uncomment hydro_inspectB_flag
! to make jacobian logs, uncomment hydro_numerical_jacobian, hydro_save_numjac_plot_data
! to do dfridr test, uncomment hydro_get_a_numerical_partial, hydro_test_partials_k,
! hydro_numerical_jacobian, hydro_save_numjac_plot_data, hydro_dump_iter_number
!hydro_get_a_numerical_partial = 1d-7
!hydro_test_partials_k = 190
!hydro_numerical_jacobian = .true.
!hydro_save_numjac_plot_data = .true.
!hydro_dump_call_number = 1
!hydro_dump_iter_number = 6
!hydro_epsder_struct = 1d-6
!hydro_epsder_chem = 1d-6
!hydro_save_photo = .true. ! Saves a photo when hydro_call_number = hydro_dump_call_number -1
!fill_arrays_with_NaNs = .true.
!report_why_dt_limits = .true.
!report_all_dt_limits = .true.
!report_hydro_dt_info = .true.
!report_dX_nuc_drop_dt_limits = .true.
!report_bad_negative_xa = .true.
!show_mesh_changes = .true.
!mesh_dump_call_number = 95
!trace_newton_bcyclic_solve_input = .true. ! input is "B" j k iter B(j,k)
!trace_newton_bcyclic_solve_output = .true. ! output is "X" j k iter X(j,k)
!trace_newton_bcyclic_matrix_input = .true.
!trace_newton_bcyclic_matrix_output = .true.
!trace_newton_bcyclic_steplo = 1 ! 1st model number to trace
!trace_newton_bcyclic_stephi = 1 ! last model number to trace
!trace_newton_bcyclic_iterlo = 2 ! 1st newton iter to trace
!trace_newton_bcyclic_iterhi = 2 ! last newton iter to trace
!trace_newton_bcyclic_nzlo = 1 ! 1st cell to trace
!trace_newton_bcyclic_nzhi = 10000 ! last cell to trace; if < 0, then use nz as nzhi
!trace_newton_bcyclic_jlo = 1 ! 1st var to trace
!trace_newton_bcyclic_jhi = 100 ! last var to trace; if < 0, then use nvar as jhi
!trace_k = 0
/ ! end of controls namelist
&pgstar
!pause = .true.
pgstar_interval = 6
Grid2_win_flag = .true.
Grid2_title = '4.165 M\d\(2281)\u Z=0.007 Classical Cepheid'
History_Panels1_xaxis_name='star_age_day'
History_Panels1_max_width = 365 ! only used if > 0. causes xmin to move with xmax.
! Grid2_file_flag = .true.
file_digits = 7
Grid2_file_dir = 'png'
Grid2_file_prefix = 'grid'
Grid2_file_interval = 5 ! output when mod(model_number,Grid2_file_interval)==0
!Profile_Panels1_show_grid = .true.
Profile_Panels1_xaxis_name = 'logtau'
Profile_Panels1_xaxis_reversed = .true.
Profile_Panels1_xmin = -101D0
Profile_Panels1_xmax = -101D0
Profile_Panels1_dymin(4) = 0.02
Profile_Panels1_yaxis_name(2) = 'avg_charge_He'
/ ! end of pgstar namelist
Yet, when I try to run the shell script in bash, inlist_rsp_RR_Lyrae remains unchanged. Instead, this occurs:
Configuring inlist to save settings before full-amplitude pulsations...
Changing model parameters...
sed: no input files
/home/nick/mesa-r11701/star/test_suite/rsp_RR_Lyrae_grid/inlist_rsp_RR_Lyrae: line 1: long-period: command not found
/home/nick/mesa-r11701/star/test_suite/rsp_RR_Lyrae_grid/inlist_rsp_RR_Lyrae: line 3: original: command not found
/home/nick/mesa-r11701/star/test_suite/rsp_RR_Lyrae_grid/inlist_rsp_RR_Lyrae: line 5: syntax error near unexpected token `&'
/home/nick/mesa-r11701/star/test_suite/rsp_RR_Lyrae_grid/inlist_rsp_RR_Lyrae: line 5: `&star_job'
Compiling MESA...
As you can see, it is mistaking the first few lines of comments in inlist_rsp_RR_Lyrae as commands. Is there something I am missing here?
The problem is
sed -i \
...
-e "s/^\([[:blank:]]*photo_directory\).*/\1 = 'photos\/photos_${fields[0]}'/i" \
#-e "s/^\([[:blank:]]*RSP_alfa\).*/\1 = ${fields[6]}/i" \
"$mesa_inlist"
Here the comment before $mesa_inlist also comments out the \ at the end of the line. Therefore the command ends at that comment and sed -i complains that there is no file given. Afterwards bash tries to execute "$mesa_inlist" (that is inlist_rsp_RR_Lyrae) as a command, which fails.
To fix the problem, simply remove the comment line.
Bye the way: With this function you can drastically improve the readability of your script.
sedi() {
mapfile -t args < <(printf -- '-e\ns/^\([[:blank:]]*%s\).*/\\1 = %s/i\n' "$#")
sed -i "${args[#]}" "$mesa_inlist"
}
Now you can replace ...
sed -i \
-e "s/^\([[:blank:]]*photo_interval\).*/\1 = 1000/i" \
-e "s/^\([[:blank:]]*profile_interval\).*/\1 = 1/i" \
-e "s/^\([[:blank:]]*history_interval\).*/\1 = 1/i" \
-e "s/^\([[:blank:]]*terminal_interval\).*/\1 = 4000/i" \
-e "s/^\([[:blank:]]*max_num_profile_models\).*/\1 = -1/i" \
-e "s/^\([[:blank:]]*RSP_max_num_periods\).*/\1 = $max_num_periods_2/i" \
"$mesa_inlist"
... by ...
sedi \
photo_interval 1000 \
profile_interval 1 \
history_interval 1 \
terminal_interval 4000 \
max_num_profile_models -1 \
RSP_max_num_periods "$max_num_periods_2"
... and so on for the other sed commands.
I have to split a very large file into N smaller files with the following constraints:
I have to split on record border
Record separator can be any character
the number of records in the resulting N files should be the same (+/- 1 record)
I can just use bash and standard coreutils (I have a working solution in Perl but we're not allowed to install Perl/Python/etc)
This is not a real constraint but - if possible - I'd like to scan the original (large) file just once.
Sort order of the resulting files is not important.
My working solution in Perl reads the original file and writes...
- the 1st record to the first file
- ...
- the Nth record to the Nth file
- the N+1 record back to the first file
- etc
So - at the end - with a single scan of the initial file I do get several smaller files with the same number of records (+/- 1).
For example, assume this is the input file:
1,1,1,1A2,2,2,2A3,
3,3,3A4,4,4,4A5,5,
5,5A6,6,6,6A7,7,7,
7,A8,8,8,8A9,9,9,9
A0,0,0,0
With record separator = 'A' and N = 3 I should get three files:
# First file:
1,1,1,1A2,2,2,2A3,
3,3,3
# Second file
4,4,4,4A5,5,
5,5A6,6,6,6
# Third file:
7,7,7,
7,A8,8,8,8A9,9,9,9
A0,0,0,0
UPDATE
Here you have the perl code. I tried to make it as simple and readable as I can:
#!/usr/bin/perl
use warnings;
use strict;
use locale;
use Getopt::Std;
#-----------------------------------------------------------------------------
# Declaring variables
#-----------------------------------------------------------------------------
my %op = (); # Command line parameters hash
my $line = 0; # Output file line number
my $fnum = 0; # Output file number
my #fout = (); # Output file names array
my #fhnd = (); # Output file handles array
my #ifiles = (); # Input file names
my $i = 0; # Loop variable
#-----------------------------------------------------------------------------
# Handling command line arguments
#-----------------------------------------------------------------------------
getopts("o:n:hvr:", \%op);
die "Usage: lfsplit [-h] -n number_of_files",
" [-o outfile_prefix] [-r rec_sep_decimal] [-v] input_file(s)\n"
if $op{h} ;
if ( #ARGV ) {
#ifiles = #ARGV ;
} else {
die "No input files...\n" ;
}
$/ = chr($op{r}) if $op{r} ;
#-----------------------------------------------------------------------------
# Setting Default values
#-----------------------------------------------------------------------------
$op{o} |= 'out_' ;
#-----------------------------------------------------------------------------
# Body - split in round-robin to $op{n} files
#-----------------------------------------------------------------------------
for ( $i = 0 ; $i < $op{n} ; $i++ ) {
local *OUT ; # Localize file glob
$fout[$i] = sprintf "%s_%04d.out", $op{o}, $i ;
open ( OUT, "> $fout[$i]" ) or
die "[lfsplit] Error writing to $fout[$i]: $!\n";
push ( #fhnd , *OUT ) ;
}
$i = 0 ;
foreach ( #ifiles ) {
print "Now reading $_ ..." if $op{v} ;
open ( IN, "< $_" ) or
die "[lfsplit] Error reading $op{i}: $!\n" ;
while ( <IN> ) {
print { $fhnd[$i] } $_ ;
$i = 0 if ++$i >= $op{n} ;
}
close IN ;
}
for ( $i = 0 ; $i < $op{n} ; $i++ ) {
close $fhnd[$i] ;
}
#-----------------------------------------------------------------------------
# Exit
#-----------------------------------------------------------------------------
exit 0 ;
Just for kicks, a pure bash solution, no external programs and no forking (I think):
#!/bin/bash
input=$1
separator=$2
outputs=$3
i=0
while read -r -d"$separator" record; do
out=$((i % outputs)).txt
if ((i < outputs)); then
: > $out
else
echo -n "$separator" >> $out
fi
echo -n "$record" >> $out
((i++))
done < $input
Sadly this will reopen every file for every output operation. I'm sure it's possible to fix this, using <> to open a file descriptor and keep it open, but using that with non-literal file descriptors is a bit of a pain.
Below is my script that acquire the MAC of the machine and store within a config file.
My problem is that within each line have the character " " and it dont print it inside the file, how can I write the file using " "
MAC=$(ifconfig eth0 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
echo "
view.sslVerificationMode = *3*
view.autoConnectDesktop = *TRUE*
view.autoConnectBroker = *TRUE*
view.kioskLogin = *TRUE*
view.nonInteractive = *TRUE*
view.fullScreen = *TRUE*
view.nomenubar = *TRUE*
view.defaultBroker = *viewcs*
view.defaultUser = *CM-${MAC//:/_}*
" > /etc/vmware/view-mandatory-config;
sed -i 's/*/"/g' /etc/vmware/view-mandatory-config
with cat and a here-doc you can use multi-line input without escaping charaters
MAC=$(ifconfig eth0 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
cat << EOT > /etc/vmware/view-mandatory-config
view.sslVerificationMode = "3"
view.autoConnectDesktop = "TRUE"
view.autoConnectBroker = "TRUE"
view.kioskLogin = "TRUE"
view.nonInteractive = "TRUE"
view.fullScreen = "TRUE"
view.nomenubar = "TRUE"
view.defaultBroker = "viewcs"
view.defaultUser = "CM-${MAC//:/_}"
EOT
cat will relay the stdin input generated by the here document to stdout, thus enabling redirection to a file
Simply escape double-quotes within the outer double-quotes by putting a backslash in front of them:
MAC=$(ifconfig eth0 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}')
echo "
view.sslVerificationMode = \"3\"
view.autoConnectDesktop = \"TRUE\"
view.autoConnectBroker = \"TRUE\"
view.kioskLogin = \"TRUE\"
view.nonInteractive = \"TRUE\"
view.fullScreen = \"TRUE\"
view.nomenubar = \"TRUE\"
view.defaultBroker = \"viewcs\"
view.defaultUser = \"CM-${MAC//:/_}\"
" > /etc/vmware/view-mandatory-config
Xcode allows you to create automated scripts for performing repetitive tasks. What scripts have you written to speed up development?
I've created three for my JSON.Framework for Cocoa and the iPhone. These take care of the following:
Creates a release Disk Image with dynamic embedded Framework, custom iPhone SDK, API documentation and some documentation files in.
Run Doxygen over the source to create an Xcode-compatible documentation set and install this. This means when you search for things in Xcode's documentation search your documentation can be found too.
Run Doxygen over the source to update a checked-in version of the API documentation in the source tree itself. This is pretty neat if you use Subversion (which it assumes) as the documentation is always up-to-date for the branch you're in. Great if you're hosting on Google Code, for example.
Beware some hard-coded project-specific values in the below. I didn't want to potentially break the scripts by editing those out. These are launched from a Custom Script Phase in Xcode. You can see how they're integrated in the Xcode project for the project linked above.
CreateDiskImage.sh:
#!/bin/sh
set -x
# Determine the project name and version
VERS=$(agvtool mvers -terse1)
# Derived names
VOLNAME=${PROJECT}_${VERS}
DISK_IMAGE=$BUILD_DIR/$VOLNAME
DISK_IMAGE_FILE=$INSTALL_DIR/$VOLNAME.dmg
# Remove old targets
rm -f $DISK_IMAGE_FILE
test -d $DISK_IMAGE && chmod -R +w $DISK_IMAGE && rm -rf $DISK_IMAGE
mkdir -p $DISK_IMAGE
# Create the Embedded framework and copy it to the disk image.
xcodebuild -target JSON -configuration Release install || exit 1
cp -p -R $INSTALL_DIR/../Frameworks/$PROJECT.framework $DISK_IMAGE
IPHONE_SDK=2.2.1
# Create the iPhone SDK directly in the disk image folder.
xcodebuild -target libjson -configuration Release -sdk iphoneos$IPHONE_SDK install \
ARCHS=armv6 \
DSTROOT=$DISK_IMAGE/SDKs/JSON/iphoneos.sdk || exit 1
sed -e "s/%PROJECT%/$PROJECT/g" \
-e "s/%VERS%/$VERS/g" \
-e "s/%IPHONE_SDK%/$IPHONE_SDK/g" \
$SOURCE_ROOT/Resources/iphoneos.sdk/SDKSettings.plist > $DISK_IMAGE/SDKs/JSON/iphoneos.sdk/SDKSettings.plist || exit 1
xcodebuild -target libjson -configuration Release -sdk iphonesimulator$IPHONE_SDK install \
ARCHS=i386 \
DSTROOT=$DISK_IMAGE/SDKs/JSON/iphonesimulator.sdk || exit 1
sed -e "s/%PROJECT%/$PROJECT/g" \
-e "s/%VERS%/$VERS/g" \
-e "s/%IPHONE_SDK%/$IPHONE_SDK/g" \
$SOURCE_ROOT/Resources/iphonesimulator.sdk/SDKSettings.plist > $DISK_IMAGE/SDKs/JSON/iphonesimulator.sdk/SDKSettings.plist || exit 1
# Allow linking statically into normal OS X apps
xcodebuild -target libjson -configuration Release -sdk macosx10.5 install \
DSTROOT=$DISK_IMAGE/SDKs/JSON/macosx.sdk || exit 1
# Copy the source verbatim into the disk image.
cp -p -R $SOURCE_ROOT/Source $DISK_IMAGE/$PROJECT
rm -rf $DISK_IMAGE/$PROJECT/.svn
# Create the documentation
xcodebuild -target Documentation -configuration Release install || exit 1
cp -p -R $INSTALL_DIR/Documentation/html $DISK_IMAGE/Documentation
rm -rf $DISK_IMAGE/Documentation/.svn
cat <<HTML > $DISK_IMAGE/Documentation.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<script type="text/javascript">
<!--
window.location = "Documentation/index.html"
//-->
</script>
</head>
<body>
<p>Aw, shucks! I tried to redirect you to the api documentation but obviously failed. Please find it yourself. </p>
</body>
</html>
HTML
cp -p $SOURCE_ROOT/README $DISK_IMAGE
cp -p $SOURCE_ROOT/Credits.rtf $DISK_IMAGE
cp -p $SOURCE_ROOT/Install.rtf $DISK_IMAGE
cp -p $SOURCE_ROOT/Changes.rtf $DISK_IMAGE
hdiutil create -fs HFS+ -volname $VOLNAME -srcfolder $DISK_IMAGE $DISK_IMAGE_FILE
InstallDocumentation.sh:
#!/bin/sh
# See also http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
set -x
VERSION=$(agvtool mvers -terse1)
DOXYFILE=$DERIVED_FILES_DIR/doxygen.config
DOXYGEN=/Applications/Doxygen.app/Contents/Resources/doxygen
DOCSET=$INSTALL_DIR/Docset
rm -rf $DOCSET
mkdir -p $DOCSET || exit 1
mkdir -p $DERIVED_FILES_DIR || exit 1
if ! test -x $DOXYGEN ; then
echo "*** Install Doxygen to get documentation generated for you automatically ***"
exit 1
fi
# Create a doxygen configuration file with only the settings we care about
$DOXYGEN -g - > $DOXYFILE
cat <<EOF >> $DOXYFILE
PROJECT_NAME = $FULL_PRODUCT_NAME
PROJECT_NUMBER = $VERSION
OUTPUT_DIRECTORY = $DOCSET
INPUT = $SOURCE_ROOT/Source
FILE_PATTERNS = *.h *.m
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_RELATIONS = YES
REPEAT_BRIEF = NO
CASE_SENSE_NAMES = YES
INLINE_INHERITED_MEMB = YES
SHOW_FILES = NO
SHOW_INCLUDE_FILES = NO
GENERATE_LATEX = NO
GENERATE_HTML = YES
GENERATE_DOCSET = YES
DOCSET_FEEDNAME = "$PROJECT.framework API Documentation"
DOCSET_BUNDLE_ID = org.brautaset.$PROJECT
EOF
# Run doxygen on the updated config file.
# doxygen creates a Makefile that does most of the heavy lifting.
$DOXYGEN $DOXYFILE
# make will invoke docsetutil. Take a look at the Makefile to see how this is done.
make -C $DOCSET/html install
# Construct a temporary applescript file to tell Xcode to load a docset.
rm -f $TEMP_DIR/loadDocSet.scpt
cat <<EOF > $TEMP_DIR/loadDocSet.scpt
tell application "Xcode"
load documentation set with path "/Users/$USER/Library/Developer/Shared/Documentation/DocSets/org.brautaset.${PROJECT}.docset/"
end tell
EOF
# Run the load-docset applescript command.
osascript $TEMP_DIR/loadDocSet.scpt
RegenerateDocumentation.sh:
#!/bin/sh
# See also http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
set -x
VERSION=$(agvtool mvers -terse1)
DOXYFILE=$DERIVED_FILES_DIR/doxygen.config
DOXYGEN=/Applications/Doxygen.app/Contents/Resources/doxygen
DOCSET=$INSTALL_DIR/Documentation
APIDOCDIR=$SOURCE_ROOT/documentation
rm -rf $DOCSET
mkdir -p $DOCSET || exit 1
mkdir -p $DERIVED_FILES_DIR || exit 1
if ! test -x $DOXYGEN ; then
echo "*** Install Doxygen to get documentation generated for you automatically ***"
exit 1
fi
# Create a doxygen configuration file with only the settings we care about
$DOXYGEN -g - > $DOXYFILE
cat <<EOF >> $DOXYFILE
PROJECT_NAME = $FULL_PRODUCT_NAME
PROJECT_NUMBER = $VERSION
OUTPUT_DIRECTORY = $DOCSET
INPUT = $SOURCE_ROOT/Source
FILE_PATTERNS = *.h *.m
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_RELATIONS = YES
REPEAT_BRIEF = NO
CASE_SENSE_NAMES = YES
INLINE_INHERITED_MEMB = YES
SHOW_FILES = NO
SHOW_INCLUDE_FILES = NO
GENERATE_LATEX = NO
GENERATE_HTML = YES
GENERATE_DOCSET = NO
EOF
# Run doxygen on the updated config file.
$DOXYGEN $DOXYFILE
# Replace the old dir with the newly generated one.
rm -f $APIDOCDIR/*
cp -p $DOCSET/html/* $APIDOCDIR
cd $APIDOCDIR
# Revert files that differ only in the timestamp.
svn diff *.html | diffstat | awk '$3 == 2 { print $1 }' | xargs svn revert
# Add/remove files from subversion.
svn st | awk '
$1 == "?" { print "svn add", $2 }
$1 == "!" { print "svn delete", $2 }
' | sh -
svn propset svn:mime-type text/html *.html
svn propset svn:mime-type text/css *.css
svn propset svn:mime-type image/png *.png
svn propset svn:mime-type image/gif *.gif
This is an improvement of the "Create Property and Synths for instance variables" script that Lawrence Johnston posted above.
Settings:
Input: Entire Document
Directory: Home Directory
Output: Discard Output
Errors: Ignore Errors (or Alert if you want to see them)
Select any number of variables and it'll create properties and syns for all of them. It'll even create/edit your dalloc method as necessary.
Edit up the results if they are not exactly right (copy vs. retain, etc.)
Handles more things like underbar storage name, behavior, dealloc,…
Link to where this comes from and discussion: http://cocoawithlove.com/2008/12/instance-variable-to-synthesized.html
#! /usr/bin/perl -w
# Created by Matt Gallagher on 20/10/08.
# Copyright 2008 Matt Gallagher. All rights reserved.
#
# Enhancements by Yung-Luen Lan and Mike Schrag on 12/08/09.
# (mainly: multiple lines)
# Copyright 2009 Yung-Luen Lan and Mike Schrag. All rights reserved.
#
# Enhancements by Pierre Bernard on 20/09/09.
# (mainly: underbar storage name, behavior, dealloc,…)
# Copyright 2009 Pierre Bernard. All rights reserved.
#
# Permission is given to use this source code file without charge in any
# project, commercial or otherwise, entirely at your risk, with the condition
# that any redistribution (in part or whole) of source code must retain
# this copyright and permission notice. Attribution in compiled projects is
# appreciated but not required.
use strict;
# Get the header file contents from Xcode user scripts
my $headerFileContents = <<'HEADERFILECONTENTS';
%%%{PBXAllText}%%%
HEADERFILECONTENTS
# Get the indices of the selection from Xcode user scripts
my $selectionStartIndex = %%%{PBXSelectionStart}%%%;
my $selectionEndIndex = %%%{PBXSelectionEnd}%%%;
# Find the closing brace (end of the class variables section)
my $remainderOfHeader = substr $headerFileContents, $selectionEndIndex;
my $indexAfterClosingBrace = $selectionEndIndex + index($remainderOfHeader, "\n}\n") + 3;
if ($indexAfterClosingBrace == -1)
{
exit 1;
}
# Get path of the header file
my $implementationFilePath = "%%%{PBXFilePath}%%%";
my $headerFilePath = $implementationFilePath;
# Look for an implemenation file with a ".m" or ".mm" extension
$implementationFilePath =~ s/\.[hm]*$/.m/;
if (!(-e $implementationFilePath))
{
$implementationFilePath =~ s/.m$/.mm/;
}
# Stop now if the implementation file can't be found
if (!(-e $implementationFilePath))
{
exit 1;
}
my $propertyDeclarations = '';
my $synthesizeStatements = '';
my $releaseStatements = '';
# Handle subroutine to trim whitespace off both ends of a string
sub trim
{
my $string = shift;
$string =~ s/^\s*(.*?)\s*$/$1/;
return $string;
}
# Get the selection out of the header file
my $selectedText = substr $headerFileContents, $selectionStartIndex, ($selectionEndIndex - $selectionStartIndex);
$selectedText = trim $selectedText;
my $selectedLine;
foreach $selectedLine (split(/\n+/, $selectedText)) {
my $type = '';
my $asterisk = '';
my $name = '';
my $behavior = '';
# Test that the selection is:
# At series of identifiers (the type name and access specifiers)
# Possibly an asterisk
# Another identifier (the variable name)
# A semi-colon
if (length($selectedLine) && ($selectedLine =~ /([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*);/))
{
$type = $1;
$type = trim $type;
$asterisk = $2;
$asterisk = trim $asterisk;
$name = $3;
$behavior = 'assign';
if (defined($asterisk) && length($asterisk) == 1)
{
if (($type eq 'NSString') || ($type eq 'NSArray') || ($type eq 'NSDictionary') || ($type eq 'NSSet'))
{
$behavior = 'copy';
}
else
{
if (($name =~ /Delegate/) || ($name =~ /delegate/) || ($type =~ /Delegate/) || ($type =~ /delegate/))
{
$behavior = 'assign';
}
else
{
$behavior = 'retain';
}
}
}
else
{
if ($type eq 'id')
{
$behavior = 'copy';
}
$asterisk = '';
}
}
else
{
next;
}
my $storageName = '';
if ($name =~ /_([_A-Za-z][_A-Za-z0-9]*)/) {
$storageName = $name;
$name = $1;
}
# Create and insert the propert declaration
my $propertyDeclaration = "\#property (nonatomic, $behavior) $type " . $asterisk . $name . ";\n";
$propertyDeclarations = $propertyDeclarations . $propertyDeclaration;
# Create and insert the synthesize statement
my $synthesizeStatement = '';
if (length($storageName))
{
$synthesizeStatement = "\#synthesize $name = $storageName;\n";
}
else
{
$synthesizeStatement = "\#synthesize $name;\n";
}
$synthesizeStatements = $synthesizeStatements . $synthesizeStatement;
# Create and insert release statement
my $releaseName = $name;
my $releaseStatement = '';
if (length($storageName))
{
$releaseName = $storageName;
}
if ($behavior eq 'assign')
{
if ($type eq 'SEL')
{
$releaseStatement = "\t$releaseName = NULL;\n";
}
}
else
{
$releaseStatement = "\t[$releaseName release];\n\t$releaseName = nil;\n";
}
$releaseStatements = $releaseStatements . $releaseStatement;
}
my $leadingNewline = '';
my $trailingNewline = '';
# Determine if we need to add a newline in front of the property declarations
if (substr($headerFileContents, $indexAfterClosingBrace, 1) eq "\n")
{
$indexAfterClosingBrace += 1;
$leadingNewline = '';
}
else
{
$leadingNewline = "\n";
}
# Determine if we need to add a newline after the property declarations
if (substr($headerFileContents, $indexAfterClosingBrace, 9) eq '#property')
{
$trailingNewline = '';
}
else
{
$trailingNewline = "\n";
}
substr($headerFileContents, $indexAfterClosingBrace, 0) = $leadingNewline . $propertyDeclarations . $trailingNewline;
my $replaceFileContentsScript = <<'REPLACEFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to (text 1 thru -2 of newDocText)
end tell
end run
REPLACEFILESCRIPT
# Use Applescript to replace the contents of the header file
# (I could have used the "Output" of the Xcode user script instead)
system 'osascript', '-e', $replaceFileContentsScript, $headerFilePath, $headerFileContents;
my $getFileContentsScript = <<'GETFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run
GETFILESCRIPT
# Get the contents of the implmentation file
open(SCRIPTFILE, '-|') || exec 'osascript', '-e', $getFileContentsScript, $implementationFilePath;
my $implementationFileContents = do {local $/; <SCRIPTFILE>};
close(SCRIPTFILE);
# Look for the class implementation statement
if (length($implementationFileContents) && ($implementationFileContents =~ /(\#implementation [_A-Za-z][_A-Za-z0-9]*\n)/))
{
my $matchString = $1;
my $indexAfterMatch = index($implementationFileContents, $matchString) + length($matchString);
# Determine if we want a newline before the synthesize statement
if (substr($implementationFileContents, $indexAfterMatch, 1) eq "\n")
{
$indexAfterMatch += 1;
$leadingNewline = '';
}
else
{
$leadingNewline = "\n";
}
# Determine if we want a newline after the synthesize statement
if (substr($implementationFileContents, $indexAfterMatch, 11) eq '#synthesize')
{
$trailingNewline = '';
}
else
{
$trailingNewline = "\n";
}
substr($implementationFileContents, $indexAfterMatch, 0) = $leadingNewline. $synthesizeStatements . $trailingNewline;
if ($implementationFileContents =~ /([ \t]*\[.*super.*dealloc.*\].*;.*\n)/)
{
my $deallocMatch = $1;
my $indexAfterDeallocMatch = index($implementationFileContents, $deallocMatch);
substr($implementationFileContents, $indexAfterDeallocMatch, 0) = "$releaseStatements\n";
}
elsif ($implementationFileContents =~ /(\#synthesize .*\n)*(\#synthesize [^\n]*\n)/s) {
my $synthesizeMatch = $2;
my $indexAfterSynthesizeMatch = index($implementationFileContents, $synthesizeMatch) + length($synthesizeMatch);
my $deallocMethod = "\n- (void)dealloc\n{\n$releaseStatements\n\t[super dealloc];\n}\n";
substr($implementationFileContents, $indexAfterSynthesizeMatch, 0) = $deallocMethod;
}
# Use Applescript to replace the contents of the implementation file in Xcode
system 'osascript', '-e', $replaceFileContentsScript, $implementationFilePath, $implementationFileContents;
}
exit 0;
Here's one to log a method and its arguments any time it's executed (Select the method definition up through the lie with the opening brace and execute the script). If FIXME shows up in the output it means it's an unrecognized type. You can either add it to the script or choose the proper format specifier manually.
#!/usr/bin/python
# LogMethod
# Selection
# Selection
# Insert after Selection
# Display in Alert
import sys
import re
input = sys.stdin.read()
methodPieces = re.findall("""(\w*:)""", input)
vars = re.findall(""":\(([^)]*)\)[ ]?(\w*)""", input)
outputStrings = ["\n NSLog(#\""]
# Method taking no parameters
if not methodPieces:
outputStrings.append(re.findall("""(\w*)[ ]?{""", input)[0])
for (methodPiece, var) in zip(methodPieces, vars):
type = var[0]
outputStrings.append(methodPiece)
if "**" in type:
outputStrings.append("%p")
elif "*" in type:
if "char" in type:
outputStrings.append("%c")
else:
outputStrings.append("%#")
else:
if "int" in type or "NSInteger" in type or "BOOL" in type:
outputStrings.append("%i")
elif "NSUInteger" in type:
outputStrings.append("%u")
elif "id" in type:
outputStrings.append("%#")
elif "NSTimeInterval" in type:
outputStrings.append("%f")
elif "SEL" in type:
outputString.append("%s")
else:
outputStrings.append('"FIXME"')
if not methodPiece == methodPieces[-1]:
outputStrings.append('\\n"\n #"')
outputStrings.append("\"")
for var in vars:
name = var[1]
outputStrings.append(",\n ")
outputStrings.append(name)
outputStrings.append(");")
print "".join(outputStrings),
Here's one to create a -description method for a class. Highlight the instance variables declaration section (#interface ... { ... }) and execute the script. Then paste the result into your implementation. I use this one along with po objectName in GDB. If FIXME shows up in the output it means it's an unrecognized type. You can either add it to the script or choose the proper format specifier manually.
#!/usr/bin/python
# Create description method for class
# Selection
# Selection
# Insert after Selection
# Display in Alert
import sys
import re
input = sys.stdin.read()
className = re.findall("""(?:#interface )(\w*)""", input)[0]
vars = re.findall("""(\w*[ ][*]?)(\w*?)_?;""", input)
outputStrings = ["- (NSString *)description {\n"]
outputStrings.append("""return [NSString stringWithFormat:#"%s :\\n"\n#" -""" % className)
for type, var in vars:
outputStrings.append("%s:" % var)
if "**" in type:
outputStrings.append("%p")
elif "*" in type:
if "char" in type:
outputStrings.append("%c")
else:
outputStrings.append("%#")
else:
if "int" in type or "NSInteger" in type or "BOOL" in type:
outputStrings.append("%i")
elif "NSUInteger" in type:
outputStrings.append("%u")
elif "id" in type:
outputStrings.append("%#")
elif "NSTimeInterval" in type:
outputStrings.append("%f")
elif "SEL" in type:
outputString.append("%s")
else:
outputStrings.append('"FIXME"')
if not var == vars[-1][1]:
outputStrings.append(',\\n"\n#" -')
outputStrings.append("\"")
for type, var in vars:
outputStrings.append(",\n")
outputStrings.append("[self %s]" % var)
outputStrings.append("];\n}")
print "".join(outputStrings),
Here's one that I found somewhere else that creates #property (copy) and #synthesize property directives for an instance variable. It could use a bit of improvement (say, to let you synthesize multiple variables at once), but it's better than creating them by hand.
Select the instance variable you want to create a property for and activate the script.
If I want a (retain) instead of (copy) I just activate the script and change it to retain manually (it's smart enough to not include the (copy) on primitive types such as int to begin with).
#! /usr/bin/perl -w
#Create property from instance variable
#Entire Document
#Home Directory
#Discard Output
#Display in Alert
use strict;
# Get the header file contents from Xcode user scripts
my $headerFileContents = <<'HEADERFILECONTENTS';
%%%{PBXAllText}%%%
HEADERFILECONTENTS
# Get the indices of the selection from Xcode user scripts
my $selectionStartIndex = %%%{PBXSelectionStart}%%%;
my $selectionEndIndex = %%%{PBXSelectionEnd}%%%;
# Get path of the header file
my $implementationFilePath = "%%%{PBXFilePath}%%%";
my $headerFilePath = $implementationFilePath;
# Look for an implemenation file with a ".m" or ".mm" extension
$implementationFilePath =~ s/\.[hm]*$/.m/;
if (!(-e $implementationFilePath))
{
$implementationFilePath =~ s/.m$/.mm/;
}
# Handle subroutine to trime whitespace off both ends of a string
sub trim
{
my $string = shift;
$string =~ s/^\s*(.*?)\s*$/$1/;
return $string;
}
# Get the selection out of the header file
my $selectedText = substr $headerFileContents, $selectionStartIndex, ($selectionEndIndex - $selectionStartIndex);
$selectedText = trim $selectedText;
my $type = "";
my $asterisk = "";
my $name = "";
my $behavior = "";
# Test that the selection is:
# At series of identifiers (the type name and access specifiers)
# Possibly an asterisk
# Another identifier (the variable name)
# A semi-colon
if (length($selectedText) && ($selectedText =~ /([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*);/))
{
$type = $1;
$type = trim $type;
$asterisk = $2;
$asterisk = trim $asterisk;
$name = $3;
$behavior = "";
if (defined($asterisk) && length($asterisk) == 1)
{
$behavior = "(copy) "; #"(nonatomic, retain) ";
}
else
{
$asterisk = "";
}
}
else
{
exit 1;
}
# Find the closing brace (end of the class variables section)
my $remainderOfHeader = substr $headerFileContents, $selectionEndIndex;
my $indexAfterClosingBrace = $selectionEndIndex + index($remainderOfHeader, "\n}\n") + 3;
if ($indexAfterClosingBrace == -1)
{
exit 1;
}
# Determine if we need to add a newline in front of the property declaration
my $leadingNewline = "\n";
if (substr($headerFileContents, $indexAfterClosingBrace, 1) eq "\n")
{
$indexAfterClosingBrace += 1;
$leadingNewline = "";
}
# Determine if we need to add a newline after the property declaration
my $trailingNewline = "\n";
if (substr($headerFileContents, $indexAfterClosingBrace, 9) eq "\#property")
{
$trailingNewline = "";
}
# Create and insert the propert declaration
my $propertyDeclaration = $leadingNewline . "\#property " . $behavior . $type . " " . $asterisk . $name . ";\n" . $trailingNewline;
substr($headerFileContents, $indexAfterClosingBrace, 0) = $propertyDeclaration;
my $replaceFileContentsScript = <<'REPLACEFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to newDocText
end tell
end run
REPLACEFILESCRIPT
# Use Applescript to replace the contents of the header file
# (I could have used the "Output" of the Xcode user script instead)
system 'osascript', '-e', $replaceFileContentsScript, $headerFilePath, $headerFileContents;
# Stop now if the implementation file can't be found
if (!(-e $implementationFilePath))
{
exit 1;
}
my $getFileContentsScript = <<'GETFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run
GETFILESCRIPT
# Get the contents of the implmentation file
open(SCRIPTFILE, '-|') || exec 'osascript', '-e', $getFileContentsScript, $implementationFilePath;
my $implementationFileContents = do {local $/; <SCRIPTFILE>};
close(SCRIPTFILE);
# Look for the class implementation statement
if (length($implementationFileContents) && ($implementationFileContents =~ /(\#implementation [_A-Za-z][_A-Za-z0-9]*\n)/))
{
my $matchString = $1;
my $indexAfterMatch = index($implementationFileContents, $matchString) + length($matchString);
# Determine if we want a newline before the synthesize statement
$leadingNewline = "\n";
if (substr($implementationFileContents, $indexAfterMatch, 1) eq "\n")
{
$indexAfterMatch += 1;
$leadingNewline = "";
}
# Determine if we want a newline after the synthesize statement
$trailingNewline = "\n";
if (substr($implementationFileContents, $indexAfterMatch, 11) eq "\#synthesize")
{
$trailingNewline = "";
}
# Create and insert the synthesize statement
my $synthesizeStatement = $leadingNewline . "\#synthesize " . $name . ";\n" . $trailingNewline;
substr($implementationFileContents, $indexAfterMatch, 0) = $synthesizeStatement;
# Use Applescript to replace the contents of the implementation file in Xcode
system 'osascript', '-e', $replaceFileContentsScript, $implementationFilePath, $implementationFileContents;
}
exit 0;
This on creates #proptery, #synthesize, dealloc, viewDidUnload and public methods for you. Easy XCode integration:
http://github.com/holtwick/xobjc