C言語でフォルダ(ディレクトリ)を丸ごとコピーする
※2012年10月1日、Ubuntu 12.10でコンパイルできるように修正。
※より複雑なコードを見たい場合はsnowcpのソースを見てやって下さい。
Linuxで動作する良さ気なサンプルコードを見つけられなかったので自分で書いてみた。実用性は皆無。
コンパイルにはGLibが必要なのでインストールしておく。
sudo apt-get install build-essential libgtk-3-dev
コンパイルするときは以下のように実行する。
gcc *.c -Wall -Wextra -O2 `pkg-config --cflags --libs gtk+-3.0` -std=c99 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
以下、ソース。4つのファイルに分割してある。
/* a.c */ #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <linux/limits.h> #include <unistd.h> #include <glib.h> void check_dir(char *src); int main(int argc, char *argv[]) { char src[PATH_MAX]; char dst[PATH_MAX]; if(argc == 3) { if(g_file_test(argv[1], G_FILE_TEST_IS_DIR) && g_file_test(argv[2], G_FILE_TEST_IS_DIR)) { if((realpath(argv[1], src) == NULL) || (realpath(argv[2], dst) == NULL)) { fprintf(stderr, "絶対パスの作成に失敗しました\n"); } else if(chdir(dst) == -1) { fprintf(stderr, "コピー先にアクセスできません\n"); } else { check_dir(src); } } else { fprintf(stderr, "引数はフォルダである必要があります\n"); } } else { puts("引数が正しくありません"); } return EXIT_SUCCESS; }
/* b.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <dirent.h> #include <glib.h> void cp_file(char *src, char *dst); void check_dir(char *src) { DIR *dp; char *src_basename = g_path_get_basename(src); if(mkdir(src_basename, 0755)) { fprintf(stderr, "フォルダの作成に失敗しました\n"); return; } else if(chdir(src_basename) == -1) { fprintf(stderr, "コピー先にアクセスできません\n"); return; } else if((dp = opendir(src)) == NULL) { fprintf(stderr,"%sを開けません\n", src); if(chdir("..") == -1) { fprintf(stderr, "コピー先にアクセスできません\n"); exit(EXIT_FAILURE); } return; } struct dirent *entry; while((entry = readdir(dp)) != NULL) { /* * 引数の最後のNULLを忘れると、missing sentinel in function call、という警告が出る。 * g_build_pathを使わない場合はstrcatやstrncatを使うことになるか。 * strlcatはglibcには無いらしいのでg_strlcatを使う手も。 * いや、sprintf使った方が楽か。 */ char *next_file = g_build_path(G_DIR_SEPARATOR_S, src, entry->d_name, NULL); if(g_file_test(next_file, G_FILE_TEST_IS_DIR)) { if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) { continue; } check_dir(next_file); } else if(g_file_test(next_file, G_FILE_TEST_IS_REGULAR)) { cp_file(next_file, entry->d_name); } if(next_file != NULL) { free(next_file); next_file = NULL; } } if(closedir(dp) == -1) { fprintf(stderr, "コピー先でエラーが発生しました\n"); } if(chdir("..") == -1) { fprintf(stderr, "コピー先にアクセスできません\n"); exit(EXIT_FAILURE); } }
/* c.c */ #define FILE_BUF 8192000 #include <stdio.h> char buf[FILE_BUF]; void ch_time_and_mod(char *src, char *dst); void cp_file(char *src, char *dst) { FILE *src_f; FILE *dst_f; int rcount = 0; int wcount = 0; printf("%s\n", dst); if((src_f = fopen(src, "rb")) == NULL) { fprintf(stderr, "%sを開けません\n", src); return; } setvbuf(src_f, NULL, _IOFBF, FILE_BUF); if((dst_f = fopen(dst, "wb")) == NULL) { fprintf(stderr, "%sを開けません\n", dst); fclose(src_f); return; } setvbuf(dst_f, NULL, _IOFBF, FILE_BUF); while((rcount = fread(buf, 1, FILE_BUF, src_f)) > 0) { if((wcount = fwrite(buf, 1, rcount, dst_f)) != rcount) { fprintf(stderr, "%sに書き込む際にエラーが発生しました\n", dst); break; } } if(fclose(src_f) == EOF) { fprintf(stderr, "コピー元ファイルを閉じる際のエラー\n"); } if(fclose(dst_f) == EOF) { fprintf(stderr, "コピー先ファイルを閉じる際のエラー\n"); } ch_time_and_mod(src, dst); }
/* d.c */ #define _GNU_SOURCE #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> void ch_time_and_mod(char *src, char *dst) { struct stat stat_buf; if(stat(src, &stat_buf) == -1) { fprintf(stderr, "%s : ファイル情報取得エラー\n", dst); return; } else { if(chmod(dst, stat_buf.st_mode) == -1) { fprintf(stderr, "%s : ファイル情報変更エラー\n", dst); } else { struct timespec time_s[2]; time_s[0].tv_sec = stat_buf.st_atime; time_s[1].tv_sec = stat_buf.st_mtime; time_s[0].tv_nsec = stat_buf.st_atim.tv_nsec; time_s[1].tv_nsec = stat_buf.st_mtim.tv_nsec; /* 第四引数は0 or AT_SYMLINK_NOFOLLOW */ if(utimensat(AT_FDCWD, dst, time_s, AT_SYMLINK_NOFOLLOW) == -1) { fprintf(stderr, "%s : ファイル情報変更エラー\n", dst); } } } }
関連:C言語でファイルをコピーする(マルチスレッド&ダイレクトI/O編)
以下は修正前の古いコード。
※2010年9月14日。ちょっとだけ修正。
ググってみたけどLinuxで動作する良さ気なサンプルコードを見つけられなかったので自分で書いてみた。実用性は皆無。2GB以上のファイルを扱うには#define _FILE_OFFSET_BITS 64が必要とか、lstat()じゃなくてlstat64()を使うとか、()utime()は廃止予定とか、勉強になった。
/* gcc -Wall -std=c99 -D_FILE_OFFSET_BITS=64 `pkg-config --cflags --libs glib-2.0` -o dir_copy dir_copy.c コンパイル時のオプションではなくdefineを使用した場合は gcc -Wall -std=c99 `pkg-config --cflags --libs glib-2.0` -o dir_copy dir_copy.c gcc -Wall -std=gnu99 `pkg-config --cflags --libs glib-2.0` -o dir_copy dir_copy.c とした場合は、#define __USE_ATFILE が要らない */ #define _FILE_OFFSET_BITS 64 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <limits.h> #define __USE_ATFILE #include <fcntl.h> #undef __USE_MISC #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> #include <utime.h> #include <glib.h> #define IO_BUF 8192000 #define FILE_BUF 8192000 #define PATH_LEN 8192 /* ヘッダファイルをincludeし忘れたり、プロトタイプ宣言が無い外部関数を呼び出した場合、 引数や戻り値の型がintになるらしい(C99の場合?古いCではプロトタイプ宣言は無いことも多いらしい)。 ↓することでwarningが出なくなるが、悪手。ちゃんとヘッダをincludeすればいいだけの話。 */ char *realpath(const char *path, char *resolved_path); void file_cp(char *from, char *to); void check_dir(char *from); char buf[IO_BUF]; /*ファイルを読み書きする際のバッファ*/ void file_cp(char *from, char *to) { struct stat stat_buf; /*ファイルの状態を入れる構造体*/ /* struct utimbuf times;*/ /*タイムスタンプ変更用構造体。utime用*/ struct timespec time_s[2]; /*タイムスタンプ変更用構造体。utimensat用*/ FILE *ffrom; FILE *tto; int rcount; int wcount; printf("%sのコピーを開始します\n", to); if((ffrom = fopen(from, "rb")) == NULL) { fprintf(stderr, "\n"); printf("コピー元ファイルを開けません\n"); return; } setvbuf(ffrom, NULL, _IOFBF, FILE_BUF); if((tto = fopen(to, "wb")) == NULL) { fprintf(stderr, "\n"); printf("コピー先ファイルを開けません\n"); return; } setvbuf(tto, NULL, _IOFBF, FILE_BUF); while((rcount = fread(buf, 1, IO_BUF, ffrom)) > 0) { if((wcount = fwrite(buf, 1, rcount, tto)) != rcount) { fprintf(stderr, "\n"); printf("ファイル書き込み時にエラーが発生しました\n"); exit(1); } } if(fclose(ffrom) == EOF) { printf("コピー元ファイルを閉じる際のエラー\n"); exit(1); } if(fclose(tto) == EOF) { printf("コピー先ファイルを閉じる際のエラー\n"); exit(1); } /*元ファイルの状態を取得*/ lstat64(from, &stat_buf); chmod(to, stat_buf.st_mode); time_s[0].tv_sec = stat_buf.st_atime; time_s[0].tv_nsec = stat_buf.st_atimensec; time_s[1].tv_sec = stat_buf.st_mtime; time_s[1].tv_nsec = stat_buf.st_mtimensec; /*第四引数は0かAT_SYMLINK_NOFOLLOW*/ utimensat(AT_FDCWD, to, time_s, AT_SYMLINK_NOFOLLOW); /* times.actime = stat_buf.st_atime; times.modtime = stat_buf.st_mtime; utime(to, ×); */ } /*引数は絶対パス*/ void check_dir(char *from) { char current_dir[PATH_LEN]; /*CWD*/ char *separator = "/"; /*パスの区切り文字. Windowsなら\*/ char *from_basename; /*コピー元ファイル名・フォルダ名*/ char *next_file; DIR *dp; struct dirent *entry; struct stat stat_buf; struct utimbuf times; /*タイムスタンプ変更用構造体*/ gboolean gb; from_basename = g_path_get_basename(from); if(mkdir(from_basename, 0755)) { puts("mkdirに失敗しました"); return; } chdir(from_basename); getcwd(current_dir, sizeof(current_dir)); printf("cwd is : %s \n", current_dir); if((dp = opendir(from)) == NULL) { fprintf(stderr,"ディレクトリを開けません : %s\n", from); return; } while((entry = readdir(dp)) != NULL) { if(next_file != NULL) { free(next_file); next_file = NULL; } /* 引数の最後のNULLを忘れると、missing sentinel in function call、という警告が出る。 g_build_pathを使わない場合はstrcatやstrncatを使うことになるか。 strlcatはglibcには無いらしいのでg_strlcatを使う手も。 いや、sprintf使った方が楽か。 */ next_file = g_build_path(separator, from, entry->d_name, NULL); gb = g_file_test(next_file, G_FILE_TEST_IS_DIR); if(gb) { if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) { continue; } check_dir(next_file); } else { file_cp(next_file, entry->d_name); } } chdir(".."); lstat(from, &stat_buf); times.actime = stat_buf.st_atime; times.modtime = stat_buf.st_mtime; utime(from_basename, ×); if(next_file != NULL) { free(next_file); next_file = NULL; } closedir(dp); } int main(int argc, char *argv[]) { gboolean gb; char current_dir[PATH_LEN]; char from[PATH_LEN]; char to[PATH_LEN]; if(argc == 3) { gb = g_file_test(argv[1], G_FILE_TEST_IS_DIR); if(gb) { gb = g_file_test(argv[2], G_FILE_TEST_IS_DIR); if(gb) { if((realpath(argv[1], from) == NULL) || (realpath(argv[2], to) == NULL)) { puts("絶対パスの作成に失敗しました"); exit(1); } getcwd(current_dir, sizeof(current_dir)); printf("cwd is : %s \n", current_dir); printf("from : %s\n", from); printf("to : %s\n", to); chdir(to); check_dir(from); } } } else { puts("引数が正しくありません"); } return 0; }
update-apt-xapian-indexがCPUを喰うバグに関して
408 :login:Penguin [sage] :2010/06/05(土) 16:40:41 id:txwwNgMs (1/2) update-apt-xapian-indexというのがものすごくうざくてしょうがないんですけど 一切起動しないようにしたいんですけど、やり方教えてください。 409 :login:Penguin [sage] :2010/06/05(土) 17:22:20 id:CwGBGHOb (4/5) >>408 これかな? https://forums.ubuntulinux.jp/viewtopic.php?pid=54268 410 :login:Penguin [sage] :2010/06/05(土) 18:10:45 ID:Kh+m5Ogq >>408 俺も困っていたので↓を試してみた。リソース消費量と実行時間が減る……らしい。 http://ubuntuforums.org/showpost.php?s=ad8f4de5110c0d759883cb436d3f8726&p=9304431&postcount=8 完全に無効化してしまいたいなら、実行できないようにする。 http://ubuntuforums.org/showpost.php?s=ad8f4de5110c0d759883cb436d3f8726&p=7047675&postcount=4 いちおう問題はないみたいだけど自己責任でよろしく。 411 :login:Penguin [sage] :2010/06/05(土) 19:14:16 id:txwwNgMs (2/2) >>409-410 ありがとうございます。 sudo chmod 644 /etc/cron.weekly/apt-xapian-index これ実行したんですけど、その場所にまだファイルが残ってるみたいなんですけど 手動でapt-xapian-indexを削除すればいいんでしょうか? 414 :login:Penguin [sage] :2010/06/05(土) 20:01:55 id:CwGBGHOb (5/5) >>411 実行権でググれ。 結論としては削除しなくていい。
monoの文字化けは/etc/fonts/conf.d/89-ttf-thai-tlwg-synthetic.confをゴニョゴニョすればいいらしい
840 :login:Penguin [sage] :2010/06/09(水) 00:13:47 id:kn9BJ246 (1/3) 低レベル化が顕著だなあ。 まさかmonoの文字化けすら直せない奴らばっかとは。 851 :login:Penguin [sage] :2010/06/09(水) 02:36:08 id:kn9BJ246 (3/3) 池沼に答え教えるのも嫌だが、普通の奴もいるだろうから正解書いとく。 まず俺は本家版しか使ってないから、池沼に合わせて日本語版落として環境作った。 インストール直後に実行すると、たしかに一部文字化けする。 /etc/fontsを見直していくと sudo rm /etc/fonts/conf.d/89-ttf* すると文字化けが直った。実際にこのファイルを見ると、 MS Sans Serifに別のフォントを問答無用で当ててて、たぶんこれが悪さしてる。 池沼以外で困ってる人は試してみてください。池沼はやるな
Ubuntu 9.10で液晶ディスプレイを設定する(GeForce編)
液晶モニター(iiyamaのProLite B2409HDS-B1 PLB2409HDS-B1)を購入したので、設定したこととかメモ。
まず、モニター自身の設定を、
- コントラスト:13
- 輝度:25
- エコモード:オフ
- 映像設定:標準
- ACR:オフ
- Gamma:Mode 1
色温度:ユーザ(RED 100・GREEN 89・BLUE 78)- 色温度:ユーザ(RED 90・GREEN 81・BLUE 70)
- 言語:日本語
にする。次に、
システム > システム管理 > NVIDIA X Server Settings
を起動し、ディスプレイガンマの調整、ガンマ補正の話、簡易ガンマ値チェッカなどのページを見ながら、X Server Color CorrectionのGammaの値を変更(上記設定でgamma=2.0に調整する場合、Gammaの値は0.850ぐらいになる)し、Confirm Currentボタンを押して終了する。ログイン時に設定を反映させるためには~/.profileの末尾にnvidia-settings -lと記述しておく必要があるらしい。
※参考:NVIDIA製GPU/ドライバ使用時の設定ツールと明るさ/コントラスト/ガンマ調整について
ついでにDPIも変更しておく。
システム > 設定 > 外観の設定 > フォント > 詳細
で、解像度を96から120 ドット/インチに変更。
今まで使っていたiiyama ProLite E431S-3と比べて目が疲れにくいような気がする。Full HDの世界を味わってしまうと、もうSXGAには戻れんなぁと思ったり。
※コントラストと輝度の初期値はそれぞれ50と100辺りになっていたと思うが、そのままでは眩しすぎるので下げる。輝度は40だと明るく20だと暗すぎる気がしたので25辺りに、コントラストはそのおよそ半分に。
※色温度は、ググると5000Kや6500Kぐらいが良いと書かれたページが見つかるが、このディスプレイにはそういった数値を指定する機能はなく、rgbで設定するようになっている。予め、
- ウォーム(RED 90・GREEN 89・BLUE 73) → 緑っぽい
- ノーマル(RED 86・GREEN 85・BLUE 90) → 黄っぽい
- クール(RED 73・GREEN 75・BLUE 90) → 青っぽい
の三つの設定が用意されているが(sRGBは置いといて)、液晶モニターの色温度というページによると5000Kというのは赤っぽい画面らしいので、自分でそれっぽく設定。
Ubuntu 9.10でext4を使用する場合は注意が必要?
307 :login:Penguin [sage] :2009/10/29(木) 22:53:35 id:ZN9lU4Hj
リリースノートに、ext4だと512MB以上のファイルが壊れるっていうバグが
Known issue になってるから、インストールするときにはext3を使って
あとでupgradeすべし。
311 :login:Penguin [sage] :2009/10/29(木) 23:14:40 id:dT8wLN/a
>>307
リンク先に、このバグが取り除けなければ、デフォルトのfile systemをext3に
ロールバックするべきだろうってなことがかかれてるけど、
結局ex4デフォでリリースなのかな?
320 :login:Penguin [sage] :2009/10/30(金) 01:14:18 id:vlr+TbD2
512MB以上のファイル壊れまくり祭り?
321 :login:Penguin [] :2009/10/30(金) 01:34:23 id:Z3mN1rLr
ファイルシステムの不具合は不味いよなぁ。
ext3使えば良いんだけど、せっかくだしfixするまで待つわ。
しかし、リリースノートの和訳でどうしてこの事に触れないんだろ?
Ubuntu 9.10 betaの印刷機能でテキストファイルやHTMLをPDF化した場合、一部の文字コードが変わる?
Ubuntu 9.04の時にあった、PDF出力時に康煕部首を含むフォントを使用すると一部の文字の文字コードが変わるバグは直っているっぽいが、代わりに一部の文字がCJK部首補助に変わってしまうバグに気づいた。鬼(U+9B3C)が⻤(U+2EE4)になっちゃったりするっぽい。
Ubuntuを家族で共有しているパソコンで使う場合の注意点
872 :login:Penguin [sage] :2008/05/16(金) 00:43:00 id:aIjgqy8s デフォルトで /home/ユーザ名 のアクセス権が755ってのと、 updatedbが有効なのは、デスクトップ用途では欠点だな。 他のユーザの~/Documentsが見れたり、locateで検索できたりすると、 あんなファイルやこんなファイルが身内にバレちゃってさぁ大変なことに。 873 :login:Penguin [sage] :2008/05/16(金) 00:55:24 id:MWAuQhQt >>872 linuxでは基本的にユーザ同士で情報を共有しようというのもあったんだよね。 ~/.bashrcや~/.emacsを観れるようにしてノウハウを公開する意図があったりして。 でもまあ、これからのデスクトップユーザ、Windowsから流れてくる人たちにとっては、 デフォルトで見えないほうがいいのかもねえ・・・ 874 :login:Penguin [] :2008/05/16(金) 01:42:48 id:kgd04gO/ >>872 最初は755でいいんだよ、そこでアクセス拒否られると、 「ああ、何かかくしてる」 ってことになるんだよ。 あんなのやこんなのをかくしたいなら、その方法を探ればOK .systemほにゃららとかにしてアクセス権変えといたらいいんだよ。
Windowsだと制限ユーザは他のユーザのマイドキュメントを見たり出来ないが、Ubuntuだと見放題なので複数のユーザで使用する場合は注意が必要と。