쉘 스크립트에서 파일이나 명령어의 결과를 while문에서 한줄 한줄 받아서 처리해보자.


환경

  • Linux 기반 시스템
  • Bash shell(/bin/bash)


방법 예제

명령어 결과를 한줄씩 처리

  • Pipeline을 이용
ls ./Test | while read line
do
	echo $line
done
ls /tmp | while read line
do
	echo $line
done


파일을 한줄씩 읽어서 처리하기

  • Redirection을 이용
while read line
do
    echo $line
done < test.txt
  • Pipeline을 이용
cat test.txt | while read line
do
    echo $line
done


참고자료

Process file or result of command in while loop line by line in shell script.


Environment and Prerequisite

  • Linux base system
  • Bash shell(/bin/bash)


Method Examples

Process command result line by line in while loop

  • Use Pipeline
ls ./Test | while read line
do
	echo $line
done
ls /tmp | while read line
do
	echo $line
done


Process file line by line in while loop

  • Use Redirection
while read line
do
    echo $line
done < test.txt
  • Use Pipeline
cat test.txt | while read line
do
    echo $line
done


Reference

업데이트(2021.02.27): 원격에 있는 파일이나 디렉토리를 복사해오는 내용 추가

Linux 기반 운영체제에서 rsync 명령어에 대해서 알아보자.


환경 및 선수조건

  • Linux
  • Bash shell(/bin/bash)


rsync 명령어

기본

  • rsync: Remote sync의 줄임말로 여러가지 옵션을 이용해 원격 또는 로컬간에 파일이나 디렉토리를 복사하는 툴입니다.
  • rcp 그리고 scp와 같이 파일이나 디렉토리를 복사할 때 사용하는 기본 내장 명령어입니다. 원격 컴퓨터에 파일이나 디렉토리를 전달할 수 있을뿐만 아니라 로컬로도 복사가 가능합니다. 반대로 원격에 있는 디렉토리나 파일들을 가져올수도 있습니다.
  • 옵션이 정말 다양하게 많으며 많은 옵션들을 이용해 다양한 기능들을 활용할 수 있습니다.(symlink 유지, 권한 유지 그리고 파일 날짜 유지와 같은 기능들)


기본 사용법

  • manual page에 있는 자료
rsync [options ...] [source] [target]

옵션

  • -v: verbosity를 높이는 옵션으로 복사하는 과정을 더 자세하게 보여줍니다.
  • -z: compress를 주는 옵션으로 파일을 복사할 때 압축해서 복사합니다.
  • -h: 사람이 읽기 쉬운 형태로 복사 결과들을 출력해줍니다.
  • -a (same as -rlptgoD): archive 모드로 -rlptgoD 옵션을 적용한것과 같습니다. 해당 옵션들은 아래서 설명하며 symlink, 권한 그리고 timestamp와 같은 속성들을 그대로 복사합는 옵션입니다.
  • -r: 디렉토리를 복사할 때 사용하는 옵션입니다.
  • -l: symlink는 symlink 형태로 복사하는 옵션입니다.
  • -p: 파일과 디렉토리들의 권한을 유지하는 옵션입니다.
  • -t: 수정시간을 유지하는 옵션입니다.
  • -g: 그룹 속성을 유지하는 옵션입니다.
  • -o: 소유자 속성을 유지하는 옵션입니다.
  • -e: 원격에서 사용할 쉘을 명시하는 옵션입니다.
  • -D (same as --devices --specials): --devices --specials의 옵션과 같습니다.
  • --devices: root 권한이 필요하며 Device 관련된 파일들을 복사해서 생성해줍니다.
  • --specials: named socket이나 fifo와 같은 특수한 파일들도 복사하는 옵션입니다.
  • -P (same as --partial --progress): --partial --progress의 옵션과 같습니다.
  • --partial: rsync는 전송중에 인터럽트가 발생하면 전송하던 파일을 삭제하는게 기본값입니다. 이 옵션을 사용하면 전송된 부분파일을 남기고 다음부분부터 재전송 할 수 있게하여 속도를 빠르게 할 수 있습니다.
  • --progress: 전송시 진행상황을 보여줍니다.


예제

  • 로컬로 파일 복사
# rsync [File Name] [Target Path]

rsync -avzhP test.txt /tmp


# 전송시 파일명 변경도 가능

rsync -avzhP test.txt /tmp/test-renamed.txt
  • 로컬로 디렉토리 복사
# rsync [Directory Name] [Target Path]
# 디렉토리 자체가 복사된다.

rsync -avzhP test-directory /tmp


# 디렉토리 내 파일들과 하위 디렉토리들 복사
# rsync [Directory Name]/ [Target Path]

rsync -avzhP test-directory/ /tmp
  • 원격에 파일 복사
# rsync [File Name] [User]@[IP Address]:[Path]

rsync -avzhP test.txt twpower-private-server:~
rsync -avzhP test.txt twpower@192.168.1.2:~


# 전송시 파일명 변경도 가능

rsync -avzhP test.txt twpower-private-server:~/test-renamed.txt
rsync -avzhP test.txt twpower@192.168.1.2:~/test-renamed.txt
  • 원격에 디렉토리 복사
# rsync [Directory Name] [User]@[IP Address]:[Path]
# 디렉토리 자체가 복사된다.

rsync -avzhP test-directory twpower-private-server:~
rsync -avzhP test-directory twpower@192.168.1.2:~


# 디렉토리 내 파일들과 하위 디렉토리들 복사
# rsync [Directory Name]/ [User]@[IP Address]:[Path]

rsync -avzhP test-directory/ twpower-private-server:~
rsync -avzhP test-directory/ twpower@192.168.1.2:~
  • 원격에 있는 파일 로컬로 가져오기
# rsync [User]@[IP Address]:[File Name] [Path]

rsync -avzhP twpower-private-server:~/test.txt .
rsync -avzhP twpower@192.168.1.2:~/test.txt .


# 전송시 파일명 변경도 가능

rsync -avzhP twpower-private-server:~/test.txt ./test-renamed.txt
rsync -avzhP twpower@192.168.1.2:~test.txt ./test-renamed.txt
  • 원격에 있는 디렉토리 로컬로 가져오기
# rsync [User]@[IP Address]:[Directory Name] [Path]
# 디렉토리 자체가 복사된다.

rsync -avzhP twpower-private-server:~/test-directory .
rsync -avzhP twpower@192.168.1.2:~/test-directory .


# 디렉토리 내 파일들과 하위 디렉토리들 복사
# rsync [User]@[IP Address]:[Directory Name]/ [Path]

rsync -avzhP twpower-private-server:~/test-directory/ .
rsync -avzhP twpower@192.168.1.2:~/test-directory/ .


추가 내용들


참고자료

Update(2021.02.27): Add contents of copying remote file or directory to local

Let’s use rsync command in Linux base OS.


Environment and Prerequisite

  • Linux
  • Bash shell(/bin/bash)


rsync command

Basic

  • rsync: It represents remote sync. It is a fast, versatile, remote (and local) file-copying tool which can use with various options.
  • It is similar file-copying tool like rcp and scp. It can copy file or directory to not only remote but also local. Also, remote file or directory can be copied to local.
  • There are so many options that can be combined to many functions.(preserve symlink, preserve permission, preserve timestamp etc)


Basic Usage

  • Contents from manual page
rsync [options ...] [source] [target]

Option

  • -v: Increase verbosity. It give more information about copying process.
  • -z: Compress files when copying.
  • -h: Print human-readable mode.
  • -a (same as -rlptgoD): Archive mode. It is same as -rlptgoD options. Those options explanation are at below of this option. It copies symlink, permission, timestamp and etc properties.
  • -r: Recurse into directories. Use it for directory copy.
  • -l: Copy symlinks as symlinks.
  • -p: Preserve permissions.
  • -t: Preserve modification times.
  • -g: Preserve group.
  • -o: Preserve owner(super-user only).
  • -e: Specify the remote shell to use.
  • -D (same as --devices --specials): Same as --devices --specials.
  • --devices: This option causes rsync to transfer character and block device files to the remote system to recreate these devices.(root only).
  • --specials: This option causes rsync to transfer special files such as named sockets and fifo.
  • -P (same as --partial --progress): Same as --partial --progress.
  • --partial: By default, rsync will delete any partially transferred file if the transfer is interrupted. This option tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster.
  • --progress: Showing the progress of the transfer.


Examples

  • Copy file to local
# rsync [File Name] [Target Path]

rsync -avzhP test.txt /tmp


# Name can be changed during transfer

rsync -avzhP test.txt /tmp/test-renamed.txt
  • Copy directory to local
# rsync [Directory Name] [Target Path]
# Directory itself is copied.

rsync -avzhP test-directory /tmp


# Copy all files and subdirectories in directory
# rsync [Directory Name]/ [Target Path]

rsync -avzhP test-directory/ /tmp
  • Copy file to remote
# rsync [File Name] [User]@[IP Address]:[Path]

rsync -avzhP test.txt twpower-private-server:~
rsync -avzhP test.txt twpower@192.168.1.2:~


# Name can be changed during transfer

rsync -avzhP test.txt twpower-private-server:~/test-renamed.txt
rsync -avzhP test.txt twpower@192.168.1.2:~/test-renamed.txt
  • Copy directory to remote
# rsync [Directory Name] [User]@[IP Address]:[Path]
# Directory itself is copied.

rsync -avzhP test-directory twpower-private-server:~
rsync -avzhP test-directory twpower@192.168.1.2:~


# Copy all files and subdirectories in directory
# rsync [Directory Name]/ [User]@[IP Address]:[Path]

rsync -avzhP test-directory/ twpower-private-server:~
rsync -avzhP test-directory/ twpower@192.168.1.2:~
  • Copy file from remote to local
# rsync [User]@[IP Address]:[File Name] [Path]

rsync -avzhP twpower-private-server:~/test.txt .
rsync -avzhP twpower@192.168.1.2:~/test.txt .


# Name can be changed during transfer

rsync -avzhP twpower-private-server:~/test.txt ./test-renamed.txt
rsync -avzhP twpower@192.168.1.2:~test.txt ./test-renamed.txt
  • Copy directory from remote to local
# rsync [User]@[IP Address]:[Directory Name] [Path]
# Directory itself is copied.

rsync -avzhP twpower-private-server:~/test-directory .
rsync -avzhP twpower@192.168.1.2:~/test-directory .


# Copy all files and subdirectories in directory
# rsync [User]@[IP Address]:[Directory Name]/ [Path]

rsync -avzhP twpower-private-server:~/test-directory/ .
rsync -avzhP twpower@192.168.1.2:~/test-directory/ .


Additional Things


Reference

BFS와 DFS를 공부해 볼 수 있는 간단한 문제를 풀어보자.


환경 및 선수조건


문제

문제 링크

  • 문제: <그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집들의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.


접근법

  • 지도에서 하나하나 조사하면서 집을 발견하면 단지로 정의하고 집의 수를 세는 방법으로 풀이가 가능합니다.
  • 하나하나 조사를 해야하기 때문에 완전탐색으로 생각할 수 있습니다.
  • 단지 안에서 집의 수를 세는건 주변으로 뻗어 나가는 방법을 사용하기 때문에 BFS나 DFS를 통해서 4방향으로 탐색을 진행하면 됨을 알 수 있습니다.


풀이법

  1. 전체의 지도를 돌면서 집이 있는지 확인하다.
  2. 집(배열의 값 == 1)을 발견하면 단지로 정의하고 ID를 부여한다.
  3. BFS 또는 DFS 탐색을 통해서 해당 단지의 집의 수를 구한다.
  4. 해당 단지의 집의 수를 다 구하게 되면 해당 집의 ID를 인덱스로 하는 배열에 추가해준다.
  5. 전체 지도의 탐색이 다 끝나면 배열을 오름차순으로 정렬하고 출력한다.


완전탐색이란?

정의

  • 완전탐색(Bruete Force): 가능한 모든 경우의 수를 다 시도해 답을 찾는 방법
  • 정확하고 확실하게 답을 찾을 수 있다는 장점이 있지만 시간이 오래 걸린다는 단점이 있다.
  • 예시: 1부터 n까지의 수를 모두 더하기, 난수에서 배열에서 특정한 수 찾기 등등…


BFS와 DFS

개념

  • DFS(깊이우선탐색): 현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색
  • BFS(너비우선탐색): 현재 정점에 연결된 가까운 점들부터 탐색
  • 위의 이미지를 참고하시면 비교가 쉽습니다. 해당 번호 순서대로 탐색을 합니다.

방법

  • DFS: 스택 또는 재귀함수로 구현
  • BFS: 큐를 이용해서 구현


풀이코드

DFS(Recursion)

  • 재귀함수를 이용한 DFS 풀이
// DFS - Recursion
void dfs_recursion(int x, int y) {

    // 함수에 들어왔으면 -> 방문으로 표시
    visited[x][y] = true;
    // 해당 단지의 집의 수를 증가시킴
    groups[group_id]++;

    // 해당 위치의 주변을 확인
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // 지도를 벗어나지 않고
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

DFS(Stack)

  • 스택을 이용한 DFS 풀이
// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // 이용할 스택, (x,y) -> (행, 열)
    s.push(make_pair(x,y)); // pair를 만들어서 stack에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;

    while(!s.empty()){

        // 스택의 top 원소 꺼내기
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    s.push(make_pair(x,y)); // push하는 경우이기 때문에 현재 위치도 넣어준다.
                    s.push(make_pair(nx,ny)); // 스택에 현재 nx,ny도 푸시
                }
            }
        }   
    }
}

BFS(Queue)

  • 큐를 이용한 DFS 풀이
// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;  

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

전체 코드

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 25

using namespace std;

// 위, 오른, 아래, 왼
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n; // 행과 열의 수
int group_id; // 단지의 번호로 첫번째 단지부터 1로 시작
int groups[MAX_SIZE * MAX_SIZE]; // 각 단지별 집의 수

int map[MAX_SIZE][MAX_SIZE]; // 지도
bool visited[MAX_SIZE][MAX_SIZE]; // 방문했는지를 표시하는 지도

// DFS - Recursion
void dfs_recursion(int x, int y) {

    // 함수에 들어왔으면 -> 방문으로 표시
    visited[x][y] = true;
    // 해당 단지의 집의 수를 증가시킴
    groups[group_id]++;

    // 해당 위치의 주변을 확인
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // 지도를 벗어나지 않고
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // 이용할 스택, (x,y) -> (행, 열)
    s.push(make_pair(x,y)); // pair를 만들어서 stack에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;

    while(!s.empty()){

        // 스택의 top 원소 꺼내기
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    s.push(make_pair(x,y)); // push하는 경우이기 때문에 현재 위치도 넣어준다.
                    s.push(make_pair(nx,ny)); // 스택에 현재 nx,ny도 푸시
                }
            }
        }   
    }
}

// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;  

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

int main (){

    scanf("%d", &n);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++)
            //입력을 1개씩 숫자로 끊어서 받습니다 -> %1d
            scanf("%1d", &map[i][j]);
    }

    // 전체 지도 탐색
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[i][j]==1 && visited[i][j]==false){

                // 해당 지역에 단지 id를 부여하고
                group_id++;

                // 탐색
                //dfs_recursion(i, j);
                //dfs_stack(i, j);
                //bfs(i, j);
            }
        }
    }

    // 단지마다 집들의 수로 오름차순 정렬
    // ID는 1부터 시작
    // 함수 사용법: https://twpower.github.io/71-use-sort-and-stable_sort-in-cpp
    sort(groups + 1, groups + group_id + 1);

    printf("%d\n", group_id);
    for (int i = 1; i <= group_id; i++) {
        printf("%d\n", groups[i]);
    }
}


참고자료