Thứ Năm, 21 tháng 9, 2017

Python 3: Tuples

1. Tuples

Tuples là một kiểu dữ liệu tương tự như với Lists. Các phần tử trong Tuples cũng được ngăn cách nhau bởi dấu ",". Tuy nhiên, không giống với Lists, Tuples mở đầu và kết thúc với dấu ngoặc đơn "( )".
Ví dụ
  1. #!/usr/bin/python
  2. tuple = ('Mido', 12, 3.15, 'Stdio')
Tuy nhiên, bạn cũng có thể bỏ đi dấu ngoặc đơn đi, python vẫn hiểu đây là một tuples.
Ví dụ
  1. #!/usr/bin/python
  2. tuple = "stdio", "python", 2 , 4
Một tuples rỗng được tạo ra khi gán cho một cặp dấu ngoặc đơn và bên trong không chứa giá trị nào.
Ví dụ
  1. #!/usr/bin/python
  2. tuple =()
Trường hợp tuples chỉ có 1 phần tử thì bên sau phần tử đó phải có dấu phẩy. Nếu không nó sẽ hiểu đây là một kiểu int. 
Ví dụ
  1. #!/usr/bin/python
  2. tuple = (2, ) # type tuples
  3. tuple = (2) # type int

Cài python 3.6 mới nhất chung python default 2.6

Down gói và build từ source với các bước như sau:

1. Yum các gói cần thiết:
     #yum install -y gcc openssl-devel zlib-devel bzip2-devel sqlite sqlite-devel

2. Yum tiếp:
     #yum groupinstall -y "Development Tools"

3. Tạo thư mục /softs và tải gói Python 3.6.2 mới nhất về và bung gói:
    #mkdir /softs
    #cd /softs && wget http://python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz
    #tar -xvf Python-3.6.2.tar.xz

4. Tạo thư mục cài python mới và tiến hành cài đặt:
     #mkdir -p /opt/python3.6
     #cd /softs/Python-3.6.2
   #./configure --prefix=/opt/python3.6
   #make && make altinstall

make altinstall sử dụng để tránh ghi đè lên python mặc định của hệ thống.

5. Check lại phiên bản python3.6 xem nhận chưa:
    #/opt/python3.6/bin/python3.6 -V

6. Trong trường hợp muốn xóa python mới:
     #cd /softs/Python-3.6.2
     #make clean
     #make uinstall
     #make -n install



Theo mình tham khảo tài liệu chúng ta có thể dùng Virtualevn để cài đặt với nhiều lợi ích, chi tiết tham khảo tại:
   - https://daynhauhoc.com/t/neu-cai-song-song-2-ban-python-2x-va-3x-thi-khi-chay-chuong-trinh-se-chon-phien-ban-nao/24300/8
   - trang chủ: http://docs.python-guide.org/en/latest/dev/virtualenvs/

   If you are using Python 2.7 I strongly recommend that you install virtualenv and learn how to use it. Virtualenv makes it possible to create isolated Python environments. If you are using Python 3.3+ then you don’t need virtualenv because that functionality is already built in.
Each isolated Python environment (also called sandbox) can have its own Python version and packages. This is very useful when you work on multiple projects or on different versions of the same project.

Create your first isolated Python environment

# Install virtualenv for Python 2.7 and create a sandbox called my27project:
pip2.7 install virtualenv
virtualenv my27project

# Use the built-in functionality in Python 3.6 to create a sandbox called my36project:
python3.6 -m venv my36project

# Check the system Python interpreter version:
python --version
# This will show Python 2.6.6

# Activate the my27project sandbox:
source my27project/bin/activate
# Check the Python version in the sandbox (it should be Python 2.7.13):
python --version
# Deactivate the sandbox:
deactivate

# Activate the my36project sandbox:
source my36project/bin/activate
# Check the Python version in the sandbox (it should be Python 3.6.2):
python --version
# Deactivate the sandbox:

deactivate

Thứ Tư, 5 tháng 7, 2017

Bash shell insert Oracle

export LD_LIBRARY_PATH=/usr/lib/oracle/12.1/client64/lib

sleep 3

# Insert Result to DB
id=$(date +%s%3N)
ip_server=$( /sbin/ifconfig | grep 'inet addr:' | grep Bcast | cut -d":" -f2 | awk '{print $1}')
time_run=$(date +"%F %H:%M:%S")
path_run="`pwd`/$0"
detail="Xuat bao cao cho Sacombank luc 5h5 sang"
result=`cat /app/mservice/bank/report/sacombank_report_new/log/$today.log | grep -E 'File Created|\[SendSFTP\] \-\-\- DONE' | wc -l`
if [[ $result -eq 2 ]]
then
        result='ok'
else
        result='notok'
fi
note=$(cat /app/mservice/bank/report/sacombank_report_new/log/$today.log | grep  -o -E 'Transactions All.*|File Uploaded' | tr '\n' ' ' | cut -d" " -f6,9)


# Call file script with Insert DB Function
source /app/mservice/Insert_DB_Oracle.sh

exit






#Insert to DB by bash shell
#!/bin/bash
export LD_LIBRARY_PATH=/usr/lib/oracle/12.1/client64/lib
userdb='operator_xxxx'
passdb='xxxxxxxxx'
ipdb='ltest-scan.xxxxx.com.vn'
nsdb='ltest.xxxxx.com.vn'


IFS=$'\n'       # make newlines the only separator
for line in $(cat /vanhanh/phongbankhac/Ketoan/lower_limit_adjust/text.txt | sed '/^$/d')
do
        ctime=$(date +%s%3N)
        #ctime=`echo $line | awk '{print $1}'`
        tid=`echo $line | awk '{print $2}'`
        number_phone=`echo $line | awk '{print $3}'`
        date_time=$(date +"%F %H:%M:%S")
        #date_time=`echo $line | awk '{print $4}'`
        wallet_type=`echo $line | awk '{print $5}'`
        amount=`echo $line | awk '{print $6}'`
        reason=`echo $line | awk '{print $7}'`

        echo $ctime $tid $number_phone $date_time $wallet_type $amount $reason
        sqlplus -s "$userdb"/"$passdb"@"$ipdb"/"$nsdb" <<-EOF
        INSERT INTO VH_LOWERLIMIT VALUES ('$ctime', '$tid','$number_phone', TO_DATE('$date_time', 'yyyy/mm/dd hh24:mi:ss' ), '$wallet_type', '$amount', '$reason');
        commit
        quit
EOF
        sleep 1
done

Thứ Ba, 4 tháng 7, 2017

LogRotate – Công cụ quản lý Log files quan trọng cần phải biết

LOGROTATE là một tiện ích tuyệt vời trên Linux giúp đơn giản hóa việc quản lý log files trên hệ thống, bao gồm xoay vòng file log, di chuyển, nén, gửi tự động… Rotate (xoay vòng) ở đây có thể hiểu là tiến trình xử lý file log cũ theo quy định trước đó (xóa/nén/move) đồng thời tạo ra file log mới.
Bằng cách thiết lập đơn giản nhưng chặt chẽ thông qua file cấu hình, Logrotate hoạt động một cách tự động, không cần can thiệp thủ công.
Log files rất quan trọng đối với quản trị viên để theo dõi tình trạng của hệ thống/ứng dụng, tuy vậy nếu quá nhiều log file sẽ khiến dung lượng ổ cứng bị quá tải cũng như gây khó khăn trong việc tìm kiếm thông tin cần thiết.


Các file log của Canhme.com

HocVPS Script đã tự động tối ưu log file với Nginx, PHP, MySQL nên các bạn không cần phải lo lắng về vấn đề dung lượng nữa khi sử dụng. Một số server dùng control panel khác thường xuyên bị tình trạng đầy ổ cứng, log file có thể lên tới vài GB.

1. Cài đặt Logrotate

Logrotate được cài đặt mặc định trên hầu hết các bản phân phối Linux. Bạn có thể kiểm tra bằng lệnh # logrotate với kết quả trả lời phiên bản đang chạy trên server
# logrotate
logrotate 3.8.7 - Copyright (C) 1995-2001 Red Hat, Inc.
Nếu chưa được cài đặt, bạn hãy cập nhật system packages và tiến hành cài đặt logrotate
Trên hệ thống Ubuntu/Debian:
# sudo apt-get update
# sudo apt-get install logrotate
Trên hệ thống CentOS/Redhat:
# sudo yum update
# sudo yum install logrotate

2. Cấu hình Logrotate

Cấu hình Logrotate được lưu tại /etc/logrotate.conf, chứa thông tin thiết lập toàn bộ log files mà Logrotate quản lý, bao gồm chu kì lặp, dung lượng file log, nén file…
# see "man logrotate" for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
dateext

# uncomment this if you want your log files compressed
#compress

# RPM packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp and btmp -- we'll rotate them here
/var/log/wtmp {
    monthly
    create 0664 root utmp
        minsize 1M
    rotate 1
}

/var/log/btmp {
    missingok
    monthly
    create 0600 root utmp
    rotate 1
}

# system-specific logs may be also be configured here.
Theo như trên, log file được rotate hàng tuần, dữ liệu log được lưu trữ trong vòng 4 file, file log mới sẽ được tạo sau khi rotate file cũ. Các log file thêm hậu tố ngày trong tên file và được nén lại bằng gzip.
Thông tin cấu hình log file của từng ứng dụng cụ thể được lưu tại /etc/logrotate.d/
Ví dụ cấu hình rotate log file cho PHP-FPM /etc/logrotate.d/php-fpm
/home/*/logs/php-fpm*.log {
        daily
        compress
        maxage 7
        missingok
        notifempty
        sharedscripts
        delaycompress
        postrotate
            /bin/kill -SIGUSR1 `cat /var/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true
        endscript
}
Ví dụ cấu hình rotate log file cho MySQL /etc/logrotate.d/mysql:
/home/*/logs/mysql*.log {
        # create 600 mysql mysql
        notifempty
        daily
        rotate 3
        maxage 7
        missingok
        compress
        postrotate
        # just if mysqld is really running
        if test -x /usr/bin/mysqladmin &&            /usr/bin/mysqladmin ping &>/dev/null
        then
           /usr/bin/mysqladmin flush-logs
        fi
        endscript
}
Ví dụ cấu hình rotate log file cho Nginx /etc/logrotate.d/nginx
/home/*/logs/access.log /home/*/logs/error.log /home/*/logs/nginx_error.log {
        daily
        missingok
        rotate 5
        maxage 7
        compress
        delaycompress
        notifempty
        create 640 nginx adm
        sharedscripts
        postrotate
                [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        endscript
}
Có thể thấy, cấu hình Logrotate với cấu trúc khá đơn giản gồm chỉ định đường dẫn file log và thiết lập các cấu hình theo sau trong dấu {}. Sau đây mình sẽ đi rõ hơn một số lựa chọn cấu hình phổ biến.

2.1. Lựa chọn Log file được rotate

Bạn có thể chỉ định cụ thể một hay nhiều file log với đường dẫn tuyệt đối của file log đó, phân biệt danh sách các log file cụ thể bằng khoảng trắng. Ví dụ:
/home/*/logs/mysql*.log
/home/*/logs/access.log /home/*/logs/error.log /home/*/logs/nginx_error.log

2.2. Rotate theo thời gian

Có 4 giá trị cấu hình tương ứng với khoảng thời gian log file sẽ được rotate.
  • Daily: mỗi ngày
  • Weekly: mỗi đầu tuần
  • Monthly: mỗi đầu tháng
  • Yearly: mỗi năm

2.3. Rotate theo dung lượng file log

Ta có thể quy định tiến trình rotate dựa vào dung lượng file, ví dụ nếu file đó đạt dung lượng 100mb thì tiến hành rotate. Các đơn vị kích thước file có thể sử dụng là K, M, G.
size 100k
size 100M
size 100G
Cấu hình rotate dựa theo dung lượng file luôn được ưu tiên cao hơn rotate dựa vào thời gian. Khi đó, nếu 1 file log được rotate theo cấu hình dung lượng file quy định thì thời gian rotate sẽ được khởi động lại mới.
Ví dụ, log file được cấu hình rotate theo tuần weekly và theo dung lượng 100mb. Tuy vậy, đến giữa tuần log file được rotate do dung lượng file đạt 100M. Khi đó, Logrotate sẽ phải đợi sang tuần kế tiếp để thực hiện rotate log, do việc rotate theo size đã bỏ qua luôn thời gian của tuần này.

2.4. Xử lý Log file trống

Tham số missingok: nếu file log vì lý do gì đấy bị mất hoặc không tồn tại *.log thì logrotate sẽ tự động di chuyển tới phần cấu hình log của file log khác mà không cần phải xuất ra thông báo lỗi. Ngược lại sẽ là cấu hình nomissingok
Tham số Notifempty: không rotate log nếu file log này trống.

2.5. Rotate theo số lượng Log file

rotate [number]
Quy định số lượng log file cũ đã được giữ lại sau khi rotate. Ví dụ: rotate7 giữ lại 7 file log cũ. Trường hợp đã có đủ 7 file log cũ thì file cũ nhất sẽ bị xóa đi để chứa file log mới được tạo.

2.6. Tự động nén Log file

Tùy chọn Compress: Logrotate sẽ nén tất cả các file log lại sau khi đã được rotate, mặc định bằng gzip.
Nếu bạn muốn sử dụng chương trình nén khác như bzip2, xz hoặc zip thì hãy đặt tên chương trình đó thành biến sau giá trị cấu hình Compresscmd xz
Tham số Delaycompress sẽ hữu dụng trong trường hợp bạn không muốn file log cũ phải nén ngay sau khi vừa được rotate. Thay vào đó, công việc nén sẽ được delay trễ hơn bằng việc sẽ nén file log cũ đó vào lần chạy rotate kế tiếp. Tùy chọn này chỉ hoạt động đi kèm chức năng compress trong file cấu hình, tức bạn phải cấu hình compress trước đó:
compress
delaycompress
Tùy chọn nocompress không sử dụng tính năng nén đối với file log cũ

2.7. Phân quyền cho Log file

Chúng ta có thể chỉ định khi logrotate xử lý xong file log cũ sẽ tạo ra file log mới có tên tương ứng để thay thế file log hiện tại. Cấu hình tham số create sẽ quy định việc file log mới tạo ra. Bạn cần đảm bảo đúng phân quyền cho file log mới sau khi rotate.
Note: Hoạt động tạo file này sẽ chạy trước script postrotate. Ví dụ:
create 660 appuser www-data
File log mới sẽ có owner là appuser và thuộc group www-data (group mà Apache thường chạy). Quyền hạn 660 cho phép cả owner và user trong cùng 1 nhóm được phép viết, sửa nội dung lên file. Điều này cho phép các ứng dụng PHP viết vào log file.
Nếu bạn không muốn tự động tạo file log mới thì sử dụng option nocreate
Tham số Dateext sẽ giúp cài đặt hậu tố của tên log file là thời gian theo cấu trúc yyyymmdd. Logs file mặc định có các giá trị hậu tố là các con số 1,2,3…

2.8. Thực thi lệnh trước hoặc sau khi rotate

LogRotate có thể chạy các lệnh trước hay sau quá trình rotate. Điều này thường được áp dụng để khởi động lại dịch vụ sử dụng file log nhằm không làm gián đoạn quá trình ghi log.
Để chạy một số lệnh trước khi quá trình rotate bắt đầu, ta đặt lệnh thực thi nằm giữa prerotate và endscript. Ví dụ:
Prerotate
Touch /var/www/html/stop.txt
Endscript
Để chạy lệnh sau khi quá trình rotate kết thúc, ta đặt lệnh thực thi nằm giữa postrotate và endscript. Ví dụ câu lệnh dưới, Apache sẽ được khởi động lại sau khi quá trình logrotate kết thúc.
Postrotate
/etc/init.d/apache2 reload > /dev/null
Endscript
Tùy chọn sharedscripts Script postrotate sẽ được chạy sau khi toàn bộ các file logs được rotate. Nếu không có tùy chọn này, postrotate script sẽ được chạy sau mỗi log file được rotate.

3. Kiểm tra LogRotate

Sau khi thiết lập xong LogRotate, bạn save lại để hệ thống tự động chạy theo thiếp lập.

3.1. Debug cấu hình LogRotate

Nếu muốn kiểm tra cấu hình chuẩn chưa, bạn dùng tham số -d (debug) đối với các file cấu hình LogRotate riêng biệt. Ví dụ, kiểm tra toàn bộ quá trình LogRotate của Nginx:
logrotate -d /etc/logrotate.d/nginx
reading config file /etc/logrotate.d/nginx
reading config info for /home/*/logs/access.log /home/*/logs/error.log /home/*/logs/nginx_error.log

Handling 1 logs

rotating pattern: /home/*/logs/access.log /home/*/logs/error.log /home/*/logs/nginx_error.log  after 1 days (5 rotations)
empty log files are not rotated, old logs are removed
considering log /home/hocvps.com/logs/access.log
  log does not need rotating
considering log /home/hocvps.com/logs/error.log
  log needs rotating
considering log /home/hocvps.com/logs/nginx_error.log
  log does not need rotating
rotating log /home/hocvps.com/logs/error.log, log->rotateCount is 5
dateext suffix '-20161216'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
previous log /home/hocvps.com/logs/error.log.1 does not exist
renaming /home/hocvps.com/logs/error.log.5.gz to /home/hocvps.com/logs/error.log.6.gz (rotatecount 5, logstart 1, i 5),
renaming /home/hocvps.com/logs/error.log.4.gz to /home/hocvps.com/logs/error.log.5.gz (rotatecount 5, logstart 1, i 4),
renaming /home/hocvps.com/logs/error.log.3.gz to /home/hocvps.com/logs/error.log.4.gz (rotatecount 5, logstart 1, i 3),
renaming /home/hocvps.com/logs/error.log.2.gz to /home/hocvps.com/logs/error.log.3.gz (rotatecount 5, logstart 1, i 2),
renaming /home/hocvps.com/logs/error.log.1.gz to /home/hocvps.com/logs/error.log.2.gz (rotatecount 5, logstart 1, i 1),
renaming /home/hocvps.com/logs/error.log.0.gz to /home/hocvps.com/logs/error.log.1.gz (rotatecount 5, logstart 1, i 0),
renaming /home/hocvps.com/logs/error.log to /home/hocvps.com/logs/error.log.1
creating new /home/hocvps.com/logs/error.log mode = 0640 uid = 498 gid = 4
running postrotate script
running script with arg /home/*/logs/access.log /home/*/logs/error.log /home/*/logs/nginx_error.log : "
                [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
"
removing old log /home/hocvps.com/logs/error.log.6.gz
error: error opening /home/hocvps.com/logs/error.log.6.gz: No such file or directory
Output như trên không xuất hiện lỗi gì, cấu hình chuẩn.

3.2. Chạy LogRotate thủ công

Trong trường hợp bạn muốn chạy ngay LogRotate, hãy dùng lệnh bên dưới:
logrotate -vf /etc/logrotate.d/nginx
Trong đó:


  • -v verbose hiển thị thêm thông tin so với thông thường, có ích khi bạn muốn dò lỗi logrotate
  • -f bắt buộc rotate ngay lập tức
    Nguồn: https://hocvps.com/logrotate/

Thứ Tư, 7 tháng 6, 2017

cx_Oracle Python List Querying with IN or any string


Hey Everyone,

So, I'm trying to query a very large table and would like to limit the results using a python list of field values, which I can successfully execute using the cx_Oracle cursor.execute function, like so

  1. import cx_Oracle  
  2. con = cx_Oracle('authentication string')  
  3. cur = con.cursor()  
  4.   
  5. IDs = ('A1', 'A2', 'A3', 'A4')  
  6.   
  7. query1 = """  
  8. SELECT *   
  9. FROM table  
  10. WHERE ID IN """+str(IDs)  
  11.   
  12. Q = cur.execute(query1)  


However, if I attempt to join a table in the SQL statement I get the following error: ORA-00907: missing right parenthesis, like so:

  1. query2 = """  
  2. SELECT *   
  3. FROM (  
  4. SELECT t1.ID, t2.*  
  5. FROM table1 as t1, table2 as t2  
  6. WHERE t1.ID = t2.ID)  
  7. WHERE ID IN """+str(IDs)  
  8.   
  9. Q = cur.execute(query2)  



Plus, when I attempt to run the query without the nested subquery I get the following error: ORA-00933: SQL command not properly ended, like so

  1. query3 = """  
  2. SELECT t1.new_ID as new_ID, t2.*  
  3. FROM table1 as t1, table2 as t2  
  4. WHERE t1.ID = t2.ID  
  5. AND t1.new_ID IN """ +str(IDs)  
  6.   
  7. Q = cur.execute(query3)  


I would appreciate any advice in this matter. I am new to using the cx_Oracle module. I assume that this is a result of user error, ie I'm coding something wrong. Thanks!

Thứ Tư, 24 tháng 5, 2017

SSH remote, do command and get and join string Python 3

def chosen_user_first(gotipupdate, total=''):
    for ip in gotipupdate.split():
        print("Da vao trong")
        # Đọc thư mục để đưa ra user cho người dùng chọn        stdout, stderr = subprocess.Popen(['ssh', "%s" % ip,                                           'ls /var/spool/cron | grep -v _bk_20 | tr "\n" "|" | sed "s/|$//g"'],                                          stdout=subprocess.PIPE).communicate()
        print("Da thuc hien xong, day la stdout")
        stdout = stdout.decode("utf-8")
        ketqua2 = (str(ip) + ':{' + str(stdout) + '}')
        total += '%s;' % ketqua2
    total = total[:-1]
    return total

Giải thích:
def chosen_user_first(gotipupdate, total=''):               => định nghĩa 1 hàm function, với total khai báo là 1 dạng chuỗi để dùng bên dưới

    for ip in gotipupdate.split():                       => Chạy vòng lặp for với từng ip được truyền vào

        print("Da vao trong")

        stdout, stderr = subprocess.Popen(['ssh', "%s" % ip,
                                           'ls /var/spool/cron | grep -v _bk_20 | tr "\n" "|" | sed "s/|$//g"'],
                                          stdout=subprocess.PIPE).communicate()
                                                                       => SSH qua remote server và lấy stdout về

        print("Da thuc hien xong, day la stdout")

        stdout = stdout.decode("utf-8")             => Chuyển về đúng định dạng, mặc định là :b'something'

        ketqua2 = (str(ip) + ':{' + str(stdout) + '}') => ghép 2 chuỗi string thành 1, cách nhau dấu ";"

        total += '%s;' % ketqua2                        => nối 1 chuỗi trong for

    total = total[:-1]                                          => Xóa ký tự dư cuối

    return ketqua2                                           => Trả về kết quả stdout của function.


--------------------------------------------------------------------------------------------------------------------------

# Truy cập vào các remote server để backup file cron và apply file mới
        sshProcess = subprocess.Popen(['ssh', "%s" % ip, ],
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      universal_newlines=True,
                                      bufsize=0)
        sshProcess.stdin.write("cd /var/spool/cron\n")
        sshProcess.stdin.write("mv /var/spool/cron/root_test /var/spool/cron/root_test_bk_%s\n" % timenow)
        sshProcess.stdin.write("mv /var/spool/cron/%s_%s_%s /var/spool/cron/root_test\n"
                               % (ip_server, user_run_cron, timenow))
        sshProcess.stdin.close()
        for line in sshProcess.stdout:
            if line == "END\n":
                break
            print(line, end="")

Giải thích:
        sshProcess = subprocess.Popen(['ssh', "%s" % ip, ],
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      universal_newlines=True,
                                      bufsize=0)
=> Dùng để ssh qua server remote với ip khai báo tại %s

        sshProcess.stdin.write("cd /var/spool/cron\n")
        sshProcess.stdin.write("mv /var/spool/cron/root_test /var/spool/cron/root_test_bk_%s\n" % timenow)
        sshProcess.stdin.write("mv /var/spool/cron/%s_%s_%s /var/spool/cron/root_test\n"
                               % (ip_server, user_run_cron, timenow))
=> Các lệnh thực thi trên remote server, với mỗi %s tương ứng với biến khai báo cuối dòng.

        sshProcess.stdin.close()
=> Đóng kết nối

        for line in sshProcess.stdout:
            if line == "END\n":
                break
            print(line, end="")
=> In



# Join 1 chuỗi trong vòng lặp for:
employee_list = ['abc', 'def']

employee_table = ''

for last_name in employee_list:
    employee_table += '%s;' % (last_name)

employee_table += ''

print(employee_table)

Giải thích: 
employee_list = ['abc', 'def']
===> Tạo 1 list

employee_table = ''
===> Khai báo đây là 1 chuỗi

for last_name in employee_list:
    employee_table += '%s;' % (last_name)
employee_table += ''
===> Join chuỗi trong vòng lặp for 

print(employee_table)
===> In ra màn hình